边看 其他博主 的博客 博弈知识汇总 边整理一下 博弈论的相关知识。
首先 说一下博弈的一些主要分类:
(一)巴什博奕(Bash Game):只有一堆n个物品,两个人轮流从这堆物品中取物,规
定每次至少取一个,最多取m个。最后取光者得胜。
分析:显然 当 n=m+1时,先手一次不能取完,后手都能直接取完。所以先手稳输。所以当n=(m+1)*r+s时,先手的只要拿掉s个或者(m+1)*k+s即可让后手面对 n=(m+1)*(r-k)的局面,这种局面是必输局。当其取k个时你就取m+1-k个,不难理解,最后一个取完的人肯定是你,所以当n=(m+1)*r+s时,(s=n%(m+1) ),先手必赢。
当n=20,m=5时的SG表:
代码:
#include <stdio.h> #include <algorithm> #include <string.h> const int maxn = 2000 + 10; int f[maxn],SG[maxn],S[maxn]; void getSG(int n){ memset(SG,0,sizeof(SG)); for(int i=1; i<=n; i++){ memset(S,0,sizeof(S)); for(int j=1; f[j]<=i&& j<maxn; j++){ S[SG[i-f[j]]] = 1; } for(int j=0; j<=n; j++) if(S[j]==0){ SG[i]=j; break; } } } int main(){ int n=20; int m=5; for(int i=1;i<=maxn;i++){ if(i<=m) f[i]=i; else f[i]=m; } getSG(n); for(int i=0;i<=n;i++) { printf("%d**%d\n",i,SG[i]); } return 0; }
所以,当 n%(m+1) 为0时先手必输,否则先手必赢。
3、变形:条件不变,改为最后取光的人输。(即可看做谁先面对只剩1颗石子的局势必输,所以问题可转化为谁先取完(n-1)颗石子的人胜利,又变成了简单的巴什博奕)
结论:当(n-1)%(m+1)==0时后手胜利。
4、题目练习:HDOJ:2188 2149 1846
(二)威佐夫博奕(Wythoff Game):有两堆各若干个物品,两个人轮流从某一堆或同
时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
·分析:用二元组表示两堆石的数量(a,b)。并称之为局势。显然,如果甲面对(0,0),甲已经输了,那么(0,0)就成为奇异局势。手动可以推出前面几个奇异局势是(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。
#include <stdio.h> #include <stdlib.h> #include <algorithm> using namespace std; const int maxn = 1000 + 10; int SG[maxn][maxn]; void get() { int n=20; for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) { int win=0,lose=0; for (int k=1;k<=min(i,j);k++)//同时从两堆中取相同多的物品 if (SG[i-k][j-k]) win++; else lose++; for (int k=1;k<=i;k++)//从第一堆中取k个 if (SG[i-k][j]) win++; else lose++; for (int k=1;k<=j;k++)//从第二堆中取k个 if (SG[i][j-k]) win++; else lose++; if (lose==0) //所有后继都是必胜态,当前是必败态 printf("%d %d\n",i,j); else SG[i][j]=1; } } int main(){ get(); return 0; }
可以看出。a是前面未出现的最小自然数,b=a+k;(k是第k个奇异局势);
奇异局势的三个性质:
1。任何自然数都包含在一个且仅有一个奇异局势中。
由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak
-1 + k-1 = bk-1 > ak-1 。所以性质1。成立。
2。任意操作都可将奇异局势变为非奇异局势。(必输局的后继全为必胜)
事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其
他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由
于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
3。采用适当的方法,可以将非奇异局势变为奇异局势。(必赢局的后继存在至少一个必败局)
假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了
奇异局势(0,0);如果a = ak ,b > bk,那么,取走b – bk个物体,即变为奇异局
势;如果 a = ak , b < bk ,则同时从两堆中拿走 ak – ab + ak个物体,变为奇异局
势( ab – ak , ab – ak+ b – ak);如果a > ak ,b= ak + k,则从第一堆中拿走多余
的数量a – ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k)
,从第二堆里面拿走 b – bj 即可;第二种,a=bj (j < k),从第二堆里面拿走 b – a
j 即可。
从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜
;反之,则后拿者取胜。
再找规律的话我们会发现,a= c* 1.618
而1.618 = (sqrt(5)+ 1) / 2 。
大家都知道0.618是黄金分割率。而威佐夫博弈正好是1.618,这就是博弈的奇妙之处!
这里的1.618最好用公式算出来。不然精确度可能会出错。
相关证明请看这篇博客:https://blog.csdn.net/wu_tongtong/article/details/79295069
练习:poj 1067
(三)尼姆博奕(Nimm Game):有三堆各若干个物品,两个人轮流从某一堆取任意多的
物品,规定每次至少取一个,多者不限,最后取光者得胜。
分析:同理用(a,b,c)表示局势,同理用SG函数打表出来
代码如下:
#include <stdio.h> #include <stdlib.h> #include <algorithm> using namespace std; const int maxn = 100 + 10; int SG[maxn][maxn][maxn]; void get() { int n=10; for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) { for(int g=0;g<=n;g++) { int win=0,lose=0; //for (int k=1;k<=min(i,j);k++)//同时从两堆中取相同多的物品 // if (SG[i-k][j-k]) win++; else lose++; for (int k=1;k<=g;k++)//从第三堆中取k个 if (SG[i][j][g-k]) win++; else lose++; for (int k=1;k<=j;k++)//从第二堆中取k个 if (SG[i][j-k][g]) win++; else lose++; for (int k=1;k<=i;k++)//从第一堆中取k个 if (SG[i-k][j][g]) win++; else lose++; if (lose==0) //所有后继都是必胜态,当前是必败态 printf("%d %d %d\n",i,j,g); else SG[i][j][g]=1; } } } int main(){ get(); return 0; }
对奇异局势进行分析:
首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是
(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情
形。
如果我们面对的是一个非必败态(a,b,c),要如何变为必败态呢?
由Nim博弈的平衡条件可知,此游戏是一个非平衡状态的Nim博弈,因此,先手在按获胜策略一定能够取得最终的胜利。具体做法有多种,先手可以从大小为12的堆中取走11枚硬币,使得游戏达到平衡(如下表)
之后,无论后手如何取子,先手在取子后仍使得游戏达到平衡。
nim博弈的另一种理解(个人觉得这个写得较好转自https://blog.csdn.net/lgdblue/article/details/15809893):
1、问题模型:有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
2、解决思路:用(a,b,c)表示某种局势,显证(0,0,0)是第一种奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。
搞定这个问题需要把必败态的规律找出:(a,b,c)是必败态等价于a^b^c=0(^表示异或运算)。
证明:(1)任何p(a,b,c)=0的局面出发的任意局面(a,b,c’);一定有p(a,b,c’)不等于0。否则可以得到c=c’。
(2)任何p(a,b,c)不等于0的局面都可以走向 p(a,b,c)=0的局面
(3)对于 (4,9,13) 这个容易验证是奇异局势
其中有两个8,两个4,两个1,非零项成对出现,这就是尼姆和为 零的本质。别人要是拿掉13里的8或者1,那你就拿掉对应的9 中的那个8或者1;别人要是拿 掉13里的4,你就拿掉4里的4; 别人如果拿掉13里的3,就把10作分解,然后想办法满 足非零项成对即可。
3、推广一:如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b< c,我们只要将 c 变为 a^b,即可,因为有如下的运算结果: a^b^(a^b)=(a^a)^(b^b)=0^0=0。要将c 变为a^b,只从 c中减去 c-(a^b)
4、推广二:当石子堆数为n堆时,则推广为当对每堆的数目进行亦或之后值为零是必败态。
5、练习:hdoj 1847
四、Fibonacci博弈
1、问题模型:
有一堆个数为n的石子,游戏双方轮流取石子,满足:
(1)先手不能在第一次把所有的石子取完;
(2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。 约定取走最后一个石子的人为赢家。
2、解决思路:
当n为Fibonacci数时,先手必败。即存在先手的必败态当且仅当石头个数为Fibonacci数。
先看看FIB数列的必败证明:
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时,结论依然成立。
对于不是FIB数,首先进行分解。
分解的时候,要取尽量大的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)]这一堆石子,且后手先取)的必败态,即先手一定可以取到这一堆的最后一颗石子。
同理可知,对于以后的每一堆,先手都可以取到这一堆的最后一颗石子,从而获得游戏的胜利。
3、练习题目:NYOJ 取石子游戏
五、公平组合博弈(Impartial Combinatori Games)
1、定义:
(1)两人参与。
(2)游戏局面的状态集合是有限。
(3)对于同一个局面,两个游戏者的可操作集合完全相同
(4)游戏者轮流进行游戏。
(5)当无法进行操作时游戏结束,此时不能进行操作的一方算输。
(6)无论游戏如何进行,总可以在有限步数之内结束。
2、模型:给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移动者判负。事实上,这个游戏可以认为是所有公平组合游戏(Impartial Combinatori Games)的抽象模型。其实,任何一个ICG都可以通过把每个局势看成一个顶点,对每个局势和它的子局势连一条有向边来抽象成这个“有向图游戏”。
3、解决思路:
现在,假定我们给出两个游戏G1 和 G2。如果我们只知道单个游戏的P-状态和N-状态我们能够正确地玩好游戏和G1 + G2吗?答案是否定的。不难看出两个P-状态的和总是P-状态,P-状态和N-状态的和总是N-状态。但是两个N-状态的和既可能是P-状态也可能是N-状态。因此,只知道单个游戏的P-状态和N-状态是不够的。
为了正确地玩好游戏和我们需要推广P-状态和N-状态,它就是Sprague-Grudy函数(或者简称为g函数)
4、Sprague-Grudy定理:
令N = {0, 1, 2, 3, ...} 为自然数的集合。Sprague-Grundy 函数给游戏中的每个状态分配了一个自然数。结点v的Grundy值等于没有在v的后继的Grundy值中出现的最小自然数.
形式上:给定一个有限子集 S ⊂ N,令mex S(最小排斥值)为没有出现在S中的最小自然数。定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Garundy函数g如下:g(x)=mex{ g(y) | y是x的后继 }。
5、性质:
(1)所有的终结点所对应的顶点,其SG值为0,因为它的后继集合是空集——所有终结点是必败点(P点)。
(2)对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0——无论如何操作,从必败点(P点)都只能进入必胜点(N点)//对手走完又只能把N留给我们。
(3)对于一个g(x)!=0的顶点,必定存在一个后继点y满足g(y)=0——从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点)//就是那种我们要走的方法。
6、应用:
(1)可选步数为1-m的连续整数,直接取模即可,SG(x) = x % (m+1);
(2)可选步数为任意步,SG(x) = x;
(3)可选步数为一系列不连续的数,用mex(计算每个节点的值)
7、练习:hdoj 1847 1536 3980