【博弈论】博弈论入门笔记(四类基础博弈+SG函数)
『博弈论定义』
博弈论又被称为对策论(Game Theory):是二人或多人在平等的对局中各自利用对方的策略变换自己的对抗策略,达到取胜目标的理论。博弈论是研究互动决策的理论。博弈可以分析自己与对手的利弊关系,从而确立自己在博弈中的优势,因此有不少博弈理论,可以帮助对弈者分析局势,从而采取相应策略,最终达到取胜的目的。
---------------------
资料参考来自:http://www.cnblogs.com/java20130726/archive/2013/05/24/3218207.html
『博弈论的原则』
1.决策主体都是理性的,最大化自己的利益;
2.每个参与人被假定为对所处换机及其他参与者的行为形成正确信念和预期
『博弈分类』
常见的博弈分为4类
(一)巴什博奕(Bash Game):只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,
最多取m个。最后取光者得胜(谁拿了最后一个谁赢)。
开始我们假设n=m+1,由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)*r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。
总之,要保持给对手留下(m+1)的倍数,就能最后获胜。
这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十
个,谁能报到100者胜。
结论:1.if(n%(m+1) != 0) ,则先手必赢
2.if(n%(m+1) == 0),则后手必赢
1. 杭电 (Brave game): http://acm.hdu.edu.cn/showproblem.php?pid=1846
2. 杭电 (Kiki's game): http://acm.hdu.edu.cn/showproblem.php?pid=2147
3. 杭电 (Public sale): http://acm.hdu.edu.cn/showproblem.php?pid=2149
4. 杭电 (选拔志愿者): http://acm.hdu.edu.cn/showproblem.php?pid=2188
通用方法:P/N分析法
P点: 必败点,某玩家位于此点,只要对方无失误,则必败。
N点: 必胜点,某玩家位于此点,只要自己无失误,则必胜。
三个定理:
一、所有终结点都是必败点P ;
二、所有一步能走到必败点P的就是N点;
三、通过一步操作只能到N点的就是P点;
以杭电的2147 Kiki's game 为例:
根据测试用例 :5 3 5 4 6 6来使用P/N分析法,先将终结点设为P,然后根据定理二和三进行推导
在分析完毕后,观察分析的结果,找到规律。可以明显的看出只要n%2 == 0 或者 m%2==0(从1开始),则是先手必胜,反之,后手必胜。
『AC代码』
#include <iostream>
using namespace std;
int main(){int n,m;while(cin>>n>>m &&!(m == 0 && n == 0)){if(n%2 == 0 || m%2 == 0)cout<<"Wonderful!\n"; elsecout<<"What a pity!\n";}return 0;
}
(二)威佐夫博奕(Wythoff Game):有两堆各若干个物品,两个人轮流从某一堆或同
时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
我们用(ak,bk)(ak ≤ bk ,k=0,1,2,…,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。
以(1,2)为例:
1.先手取1中的一个,后手取2中的两个,先手输。
2.先手取2中的一个,后手取1中和2中各一个,先手输。
3.先手取2中的两个,后手去1中的一个,先手输。
4.先手取1和2中各一个,后手取2中剩下的一个,先手输。
可以看出无论玩家怎么取,面对奇异局势是必输的。
如何找出所有的奇异局势
下面证明,根据下面的方法,可以构造出所有的必败态:
- a. (0,0)是必败态。
- b.第k个必败态的两个数相差为k(记(0,0)为第0个必败态)。
- c. 已知前k个必败态,则最小的没出现过的正整数为第k+1个必败态的第一个数。
举个栗子:
(0,0)、(1,2)、(3,5)、(4,7) 中含有0,1,2,3,4,5,7缺少一个6,所以下一个奇异局势的前一个数就是6。
同时奇异局势有如下三条性质:
- a.任何自然数都包含在一个且仅有一个奇异局势中。
由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 。所以性质1。成立。
- b.任意操作都可将奇异局势变为非奇异局势。
事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
- c.采用适当的方法,可以将非奇异局势变为奇异局势。
大牛们总结出了一个求奇异局势的公式:
第一个值 = (int)(差值 x 1.618) (1.618 = (1+√5)/2)
即ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,…,n 方括号表示向下取整)
奇妙的是其中出现了黄金分割数(1+√5)/2 = 1.618…,因此,由ak,bk组成的矩形近似为黄金矩形,具体证明可以Beatty定理
可参考博客:http://blog.sina.com.cn/s/blog_a661ecd501017out.html
结论:1.(int)((bk-ak) * (1+√5)/2) != ak,先手必赢
2.(int)((bk-ak) * (1+√5)/2) == ak,后手必赢
北大(取石子游戏):http://poj.org/problem?id=1067
杭电 (取(2堆)石子游戏):http://acm.hdu.edu.cn/showproblem.php?pid=2177
『POJ 1067 AC代码』
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int a,b;
int main(){while(scanf("%d %d",&a,&b) != EOF){if(a >b)swap(a,b); if((int)((b-a) * (sqrt(5.0)+1)/2) == a){//注意此处sqrt()函数,将5.0改为5在POJ会有编译错误,因为函数原型中没有sqrt(int)//所以需要明确给出参数类型cout<<"0\n"; }elsecout<<"1\n";}return 0;
}
(三)尼姆博奕(Nimm Game):有n堆各若干个物品,两个人轮流从某一堆取任意多的
物品,规定每次至少取一个,多者不限,最后取光者得胜。
尼姆博弈与二进制有密切关系,假设我们n选取3,用(a,b,c)表示某种局势,(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情形。
计算机算法里面有一种叫做异或的运算,我们用符号XOR表示这种运算。其运算规则为:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
以(1,2,3)的异或运算:
1 =二进制01
2 =二进制10
3 =二进制11 (+)
———————
0 =二进制00 (注意不进位)
对于奇异局势(0,n,n)也一样,结果也是0。
任何奇异局势(a,b,c)都有a XOR b XOR c =0。
到此,大牛们得到一个结论:1.a1 XOR a2 XOR a3 ......XOR an != 0 必胜态
2.a1 XOR a2 XOR a3 ......XOR an == 0 必败态
杭电(Being a Good Boy in Spring Festival):http://acm.hdu.edu.cn/showproblem.php?pid=1850
北大(Matches Game):http://poj.org/problem?id=2234
洛谷(取火柴游戏):https://www.luogu.org/problemnew/solution/P1247?page=2
尼姆博弈有两个性质:
1.必败态只能转移到必胜态
2.必胜态总能转移到某个必败态
性质1很好证明,一旦从XOR为零的状态取走至少一颗物品,XOR一定或变成非零,此时一定是必胜态。
主要是证明性质2,观察XOR中二进制表示最高位的1,选取物品数的二进制表示对应为也为1的某堆物品。只要从中取走使得该位变为0,且其余XOR中的1也反转的数量的物品,XOR就能变成0。
若当前处于必胜态,即a1^a2^...^an!=0,那么一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。设a1^a2^...^an=k,则一定存在某个ai,k的最高位1来自于ai。这时ai^k<ai一定成立(因为异或运算之后,原本k上最高位的1会变为0)。则我们可以将ai改变成ai'=ai^k,此时a1^a2^...^ai'^...^an=a1^a2^...^an^k=0
举个栗子:
以HDOJ 1850的样例为例
三堆牌,分别为5,7,9张
5=二进制0101
7=二进制0111
9=二进制1001
———————
k=二进制1011
此时k的最高位为最左边的1,此时ai =9,k最高位的1就是来自9的最左边的1,ai' = ai^k = 0010 < ai ,将ai变成ai'即是将原本有9张的牌堆抽的只剩2张,使得a1^a2^...^ai'^...^an=a1^a2^...^an^k=0,到此性质2可证明成立。
『POJ 2234代码』
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#define MAX
using namespace std;
int main(){int m,x;int arr[25];while(scanf("%d",&m) != EOF){x = 0;for(int i = 0 ; i <m ;i++){cin>>arr[i];x^=arr[i];}if(x == 0)cout<<"No\n";//必输态 else{cout<<"Yes\n";}}return 0;
}
(四)斐波那契博弈(Fibonacci Nim):有一堆个数为n的石子,游戏双方轮流取石子,满足:
1)先手不能在第一次把所有的石子取完;
2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。
约定取走最后一个石子的人为赢家。
结论:当n为Fibonacci数的时候,必败。
f[i]:1,2,3,5,8,13,21,34,55,89……
哈工大新生同步赛(小乐乐吃糖豆):https://ac.nowcoder.com/acm/contest/302/G
杭电 (取石子游戏):http://acm.hdu.edu.cn/showproblem.php?pid=2516
『小乐乐吃糖豆AC代码』
#include <iostream>
#include <cstdio>using namespace std;int fib[50];
int main(){fib[0]=1;fib[1]=2;for(int i=2;i<45;i++)fib[i]=fib[i-1]+fib[i-2];int n;while(scanf("%d",&n)!=EOF&&n){int i=0;for(i=0;i<45;i++)if(fib[i]==n)break;if(i<45)cout<<"Big\n";elsecout<<"Small\n";}return 0;
}
可用数学归纳法证明:
为了方便,我们将n记为f[i]。
1、当i=2时,先手只能取1颗,显然必败,结论成立。
2、假设当i<=k时,结论成立。
则当i=k+1时,f[i] = f[k]+f[k-1]。
则我们可以把这一堆石子看成两堆,简称k堆和k-1堆。
(一定可以看成两堆,因为假如先手第一次取的石子数大于或等于f[k-1],则后手可以直接取完f[k],因为f[k] < 2*f[k-1])
对于k-1堆,由假设可知,不论先手怎样取,后手总能取到最后一颗。下面我们分析一下后手最后取的石子数x的情况。
如果先手第一次取的石子数y>=f[k-1]/3,则这小堆所剩的石子数小于2y,即后手可以直接取完,此时x=f[k-1]-y,则x<=2/3*f[k-1]。
我们来比较一下2/3*f[k-1]与1/2*f[k]的大小。即4*f[k-1]与3*f[k]的大小,对两值作差后不难得出,后者大。
所以我们得到,x<1/2*f[k]。
即后手取完k-1堆后,先手不能一下取完k堆,所以游戏规则没有改变,则由假设可知,对于k堆,后手仍能取到最后一颗,所以后手必胜。
即i=k+1时,结论依然成立。
那么,当n不是Fibonacci数的时候,情况又是怎样的呢?
这里需要借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。
关于这个定理的证明,感兴趣的同学可以在网上搜索相关资料。
分解的时候,要取尽量大的Fibonacci数。
比如分解85:85在55和89之间,于是可以写成85=55+30,然后继续分解30,30在21和34之间,所以可以写成30=21+9,
依此类推,最后分解成85=55+21+8+1。
则我们可以把n写成 n = f[a1]+f[a2]+……+f[ap]。(a1>a2>……>ap)
我们令先手先取完f[ap],即最小的这一堆。由于各个f之间不连续,则a(p-1) > ap + 1,则有f[a(p-1)] > 2*f[ap]。即后手只能取f[a(p-1)]这一堆,且不能一次取完。
此时后手相当于面临这个子游戏(只有f[a(p-1)]这一堆石子,且后手先取)的必败态,即先手一定可以取到这一堆的最后一颗石子。
同理可知,对于以后的每一堆,先手都可以取到这一堆的最后一颗石子,从而获得游戏的胜利。
---------------------
资料参考来自:https://blog.csdn.net/dgq8211/article/details/7602807
博弈的王道——『Sprague-Grundy函数和Sprague-Grundy定理』
SG函数:
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于任意状态 x (玩家当前面临的石子个数), 定义 SG(x) = mex(S),其中 S 是 x 后继状态的SG函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}。 这样 集合S 的终态必然是空集,所以SG函数的终态为 SG(x) = 0,当且仅当 x 为必败点P时。
结论:
1.当SG[x] = 0时,x为必败状态。
2.当SG[x] > 0时,x为必胜状态。
【实例】取石子问题
有1堆n个的石子,每次只能取{ 1, 3, 4 }个石子,先取完石子者胜利,那么各个数的SG值为多少?
SG[0]=0,f[]={1,3,4},
x=1 时,可以取走1 - f{1}个石子,剩余{0}个,所以 SG[1] = mex{ SG[0] }= mex{0} = 1;
x=2 时,可以取走2 - f{1}个石子,剩余{1}个,所以 SG[2] = mex{ SG[1] }= mex{1} = 0;
x=3 时,可以取走3 - f{1,3}个石子,剩余{2,0}个,所以 SG[3] = mex{SG[2],SG[0]} = mex{0,0} =1;
x=4 时,可以取走4- f{1,3,4}个石子,剩余{3,1,0}个,所以 SG[4] = mex{SG[3],SG[1],SG[0]} = mex{1,1,0} = 2;
x=5 时,可以取走5 - f{1,3,4}个石子,剩余{4,2,1}个,所以SG[5] = mex{SG[4],SG[2],SG[1]} =mex{2,0,1} = 3;
下面以x = 5时做样例分析:
当玩家面对还有5个石子的状态时,他可取{1,3,4}个石子,那么5的后继状态集合就是{4,2,1}。
那么mex{SG[4],SG[2],SG[1]} =mex{2,0,1} = 3 ,可得出SG[5] = 3 > 0,为必胜态。
以此类推.....
x 0 1 2 3 4 5 6 7 8....
SG[x] 0 1 0 1 2 3 2 0 1....
由上述实例我们就可以得到SG函数值求解步骤,那么计算1~n的SG函数值步骤如下:
1、使用 数组f []将 可改变当前状态 的方式记录下来。
2、然后我们使用 另一个数组S[] 将当前状态x 的后继状态标记。
3、最后模拟mex运算,也就是我们在标记值中 搜索 未被标记值 的最小值,将其赋值给SG(x)。
4、我们不断的重复 2 - 3 的步骤,就完成了 计算1~n 的函数值。
SG 定理就是:SG(G)=SG(G1)^SG(G2)^...^SG(Gn)。也就是说,原游戏的SG函数值是它的所有子游戏的SG函数值的异或。
解题模型:
1.把原游戏分解成多个独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或。
即SG(G)=SG(G1)^SG(G2)^...^Sg(Gn)。
2.分别考虑每一个子游戏,计算其SG值。
SG值的计算方法:(重点)
a.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1)(Bash game)。
b.可选步数为任意步,SG(x) = x(Nim game)。
c.可选步数为一系列不连续的数,用模板计算。
i.打表模板
int f[N],SG[MAXN],S[MAXN];//f[] - 可改变当前状态 的方式 S[] - 当前状态的后继状态集合
//打表
void getSG(int n) {int i,j;memset(SG,0,sizeof(SG));for(i = 1; i <= n; i++) { memset(S,0,sizeof(S));for(j = 0; f[j] <= i && j <= N; j++)S[SG[i-f[j]]] = 1;//S[]数组来保存当前状态的后继状态集合for(j = 0;; j++) if(!S[j]) {//模拟mex运算SG[i] = j;break;}}
}
ii.深搜模板
//注意 f数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//n是集合f的大小 f[i]是定义的特殊取法规则的数组
int f[110],SG[10010],n;
int SG_dfs(int x)
{int i;if(SG[x]!=-1)return SG[x];bool vis[110];memset(vis,0,sizeof(vis));for(i=0;i<n;i++){if(x>=f[i]){SG_dfs(x-f[i]);vis[SG[x-f[i]]]=1;}}int e;for(i=0;;i++)if(!vis[i]){e=i;break;}return SG[x]=e;
}
3.若SG(G)=SG(G1)^SG(G2)^...^SG(Gn) > 0,局势为N,先手必胜,反之局势为P,先手必败。
普遍优先使用打表法,只用在打表无法使用的时候再使用深搜。
---------------------
资料参考来自:https://blog.csdn.net/strangedbly/article/details/51133186
杭电(Fibonacci again and again):http://acm.hdu.edu.cn/showproblem.php?pid=1848
HDOJ 1848
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 17
#define MAX 1005
using namespace std;
int f[N],SG[MAX],S[MAX],x = 0;
void getSG(int n) {memset(SG,0,sizeof(SG));//for(int i = 1; i <= n; i++) {memset(S,0,sizeof(S));// for(int j = 0; j <= N && i-f[j] >= 0; j++) {S[SG[i-f[j]]] = 1;}for(int j = 0 ; j <= 1000; j++) {if(!S[j]) {SG[i] = j;break;}}}}
int main() {f[0] = 1;f[1] = 1;for(int i = 2 ; i <= N; i++) {f[i] = f[i-1]+f[i-2];}getSG(1001);int m,n,p;while(scanf("%d %d %d",&m,&n,&p) && (m||n||p)) {if(SG[n]^SG[m]^SG[p])cout<<"Fibo\n";elsecout<<"Nacci\n";}return 0;
}
【博弈论】博弈论入门笔记(四类基础博弈+SG函数)相关推荐
- 【博弈论】博弈论入门笔记(四类基础博弈结论+SG函数)
1.巴什博奕(Bash Game): 只有一堆n个物品,两个人轮流轮流从中取物,每次最少取一个,最多取m个,最后取光的人获胜.(谁拿了最后一个谁赢) 结论: 1.if(n%(m+1) != 0) ,则 ...
- [Java入门笔记] 面向对象编程基础(二):方法详解
2019独角兽企业重金招聘Python工程师标准>>> 什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能 ...
- 博弈论(Nim游戏、有向图游戏之SG函数)
这里写目录标题 经典NIM游戏 Nim游戏属于公平组合游戏ICG 有向图游戏(SG函数) Mex运算 SG函数 单个有向图(一堆石子) 求SG值(记忆化递归) 有向图游戏的和 ,(多个有向图(多堆石子 ...
- 数学基础(四)博弈论(巴什博弈~威佐夫博弈(黄金分割率)~尼姆博奕~斐波那契博弈~SG函数模板)
一.巴什博弈 1.问题模型 只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个,最后取光者得胜. 2.解决思路: 当n=m+1时,由于一次最多只能取m个,所以无论先取者拿走多 ...
- 机器学习入门 笔记(二) 机器学习基础概念
第二章 机器学习基础概念 1.机器的数据 2.机器学习的主要任务 3.监督学习和非监督学习 4.批量.在线学习.参数.非参数学习 5.哲学思考 6.环境的搭建 1.机器的数据 我们以鸢尾花的数据为例. ...
- java实型常量用十六进制表示_[Java入门笔记] Java语言基础(二):常量、变量与数据类型...
常量与变量 什么是常量和变量 常量与变量都是程序在运行时用来存储数据一块内存空间 常量: 常量的值在程序运行时不能被改变,Java中声明常量必须使用final关键字.常量还可以分为两种意思: 第1种意 ...
- [Java入门笔记] Java语言基础(二):常量、变量与数据类型
2019独角兽企业重金招聘Python工程师标准>>> 常量与变量 什么是常量和变量 常量与变量都是程序在运行时用来存储数据一块内存空间 常量: 常量的值在程序运行时不能被改变,Ja ...
- 点分治问题 ----------- P3727 曼哈顿计划E[点分治+博弈SG函数打表找规律]
题目链接 解题思路: 1.首先对于每个操作我们实际上是一个博弈问题 对于k=1的操作就是很基础的NIM游戏就是找到一条链的异或和为0 对于k=2的操作通过达打表找规律: 如果s是奇数那么偶数的SG函数 ...
- HDU 1404 Digital Deletions(博弈 + SG函数打表)
Digital Deletions 思路 一道博弈论的题目,考虑到题目所给的范围是字符长度为1−>61-> 61−>6,所以我们可以考虑暴力打表出10610 ^ 6106内的所有状态 ...
最新文章
- centos7下的FastDFS5.09的安装与使用
- mysql 高并发加锁_Mysql高并发加锁事务处理
- Win7上防火墙开放FTP服务以及ping解决方案
- 医学影像设备学_【技士/师证考试宝典】第四篇 医学影像设备学2
- 《实施Cisco统一通信管理器(CIPT2)》一1.6 拨号计划方面面临的挑战
- ZJOI2019一试翻车记
- JVM性能调优中的命令总结
- x79主板bios设置中文_bios菜单
- python数字排列组合去重_排列组合-生成集合的所有子集
- fullcalendar自定义搜索框_??这款搜索神器,真希望你早点使用,越早越好!有效提升学习和生活的幸福感!
- 10 部顶级数学纪录片
- 2020如何成功注册google
- matlab绘制图形中,常用函数调用(num2str,disp,gcf,hold on,plot,axis,subplot,line,stairs,grid,set,gca)
- java 四分位算法_四分位数怎么算
- 小工具--浏览器主页被挟持,svchost.exe占用网速,treeSizeFree,桌面日历,WIN自带哈希校验
- phyton方面相关书籍
- AOSP、AOKP、CM ROM 究竟有哪些区别
- alios是安卓吗_全面了解AliOS、Android、QNX三大系统
- python科学计算最佳实践_Python科学计算最佳实践:SciPy指南
- project下build.gradle文件和module下buil.gradle
热门文章
- (数据库-MySQL)查看表的结构、表的创建过程、表
- android 耳机监听权限,android 耳机监听
- win7 如何锁定计算机,Win7系统如何锁定计算机
- ACM-ICPC Jiaozuo Onsite 2018 Resistors in Parallel (思维+java大数+找规律)
- Taro小程序组件传值
- 【通信协议】1-Wire 单总线
- 深度神经网络:WX+b-vs-XW+b
- C# 调用C++dll(以基恩士LKG5000为例)
- 水源热泵机组变流量水系统节能优化探讨
- JVM设置对象直接进入年老代