边看 其他博主 的博客 博弈知识汇总 边整理一下 博弈论的相关知识。
首先 说一下博弈的一些主要分类:
(一)巴什博奕(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%(m+1) 为0时先手必输,否则先手必赢。
(二)威佐夫博奕(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
(三)尼姆博奕(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枚硬币,使得游戏达到平衡(如下表)
之后,无论后手如何取子,先手在取子后仍使得游戏达到平衡。