ACM 博弈知识 初步整理

边看  其他博主 的博客   博弈知识汇总  边整理一下 博弈论的相关知识。

首先 说一下博弈的一些主要分类:

(一)巴什博奕(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;
}
View Code

可以看出。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,b),怎样判断它是不是奇异局势呢?
我们有如下公式:a表示第一堆的个数,b表示第二堆的个数,c表示二者之差,当然a必须小于b;

再找规律的话我们会发现,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;
}
View Code

  对奇异局势进行分析:

  首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是
(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情
形。

那这种奇异局势有什么特点呢?
也不知谁这么牛逼,竟然能把这种局势和二进制联系在一起
这里说一种运算符号,异或'^',a^b=a'b+ab'(a'为非a)

 

我们用符号XOR表示这种运算,这种运算和一般加法不同的一点是1 XOR 1 = 0。先看(1,2,3)的按位模2加的结果:
1 = 二进制01
2 = 二进制10
3 = 二进制11  XOR
———————
0 = 二进制00 (注意不进位)
 
对于奇异局势(0,n,n)也一样,结果也是0
任何奇异局势(a,b,c)都有a XOR b XOR c = 0
 

如果我们面对的是一个非必败态(a,b,c),要如何变为必败态呢?

假设 a < b < c,我们只要将 c 变为a XOR b,即可。因为有如下的运算结果:
a XOR b XOR (a XOR b)=(a XOR a) XOR (b XOR b) = 0 XOR 0 = 0
要将c 变为a XOR b,只要对 c进行 c-(a XOR b)这样的运算即可。
 
为了进一步理解Nim取物品游戏,我们看看特殊情况。
如果游戏开始时只有一堆物品,先手则通过取走所有的物品而获胜。现在设有2堆物品,且物品数量分别为N1和N2。游戏者取得胜利并不在于N1和N2的值具体是多少,而是取决于它们是否相等。也就说两堆的策略我们有了,现在我们如何从两堆的取子策略扩展到任意堆数中呢?
 
首先回忆一下,每个正整数都有对应的一个二进制数,例如:57(10) = 111001(2) ,即:57(10)=2^5+2^4+2^3+2^0。于是,我们可以认为每一堆物品数由2的幂数的子堆组成。这样,含有57枚物品大堆就能看成是分别由数量为25、24、23、20的各个子堆组成。
 
现在考虑各大堆大小分别为N1,N2,……Nk的一般的Nim博弈。将每一个数Ni表示为其二进制数(数的位数相等,不等时在前面补0):
N1 = as…a1a0
N2 = bs…b1b0
……
Nk = ms…m1m0
如果每一种大小的子堆的个数都是偶数,我们就称Nim博弈是平衡的,而对应位相加是偶数的称为平衡位,否则称为非平衡位。因此,Nim博弈是平衡的,当且仅当:
as +bs + … + ms 是偶数,即as XOR bs XOR … XOR ms  = 0
……
a1 +b1 + … + m1 是偶数,即a1 XOR b1 XOR … XOR m1 = 0
a0 +b0 + … + m0是偶数,即a0 XOR b0 XOR … XOR m0 = 0
  
于是,我们就能得出尼姆博弈中先手获胜策略:
Bouton定理先手能够在非平衡尼姆博弈中取胜,而后手能够在平衡的尼姆博弈中取胜。即状态(x1, x2, x3, …, xn)为P状态当且仅当x1 xor x2 xor x3 xor … xor xn =0。这样的操作也称为Nim和(Nim Sum)。
        我们以一个两堆物品的尼姆博弈作为试验。设游戏开始时游戏处于非平衡状态。这样,先手就能通过一种取子方式使得他取子后留给后手的是一个平衡状态下的游戏,接着无论后手如何取子,再留给先手的一定是一个非平衡状态游戏,如此反复进行,当后手在最后一次平衡状态下取子后,先手便能一次性取走所有的物品而获胜。而如果游戏开始时游戏牌平衡状态,那根据上述方式取子,最终后手能获。
 
下面应用此获胜策略来考虑4堆的Nim博弈。其中各堆的大小分别为7,9,12,15枚硬币。用二进制表示各数分别为:0111,1001,1100和1111。
于是可得到如下一表:

 由Nim博弈的平衡条件可知,此游戏是一个非平衡状态的Nim博弈,因此,先手在按获胜策略一定能够取得最终的胜利。具体做法有多种,先手可以从大小为12的堆中取走11枚硬币,使得游戏达到平衡(如下表)

之后,无论后手如何取子,先手在取子后仍使得游戏达到平衡。

同样的道理,先手也可以选择大小为9的堆并取走5枚硬币而剩下4枚,或者,先手从大小为15的堆中取走13枚而留下2枚。
归根结底, Nim博弈的关键在于游戏开始时游戏处于何种状态(平衡或非平衡)和先手是否能够按照取子游戏的获胜策略来进行游戏
当堆数大于2时,我们看出Bouton定理依旧适用,下面用数学归纳法证明
  
证明:如果每堆都为0,显然是P状态(必败)。下面验证P状态和N状态的后两个递推关系:
一、每个N状态都可以一步到达P状态。
证明是构造性的。检查Nim和X的二进制表示中最左边一个1,则随便挑一个该位为1的物品堆Y,根据Nim和进行调整(0变1,1变0)即可。例如Nim和为100101011,而其中有一堆为101110001。为了让Nim和变为0,只需要让操作的物品堆Y和Nim的和异或,然后先手在第一次在Y中取走Y-(X xor Y) 个数目即可。显然操作后物品数变小,因此和合法的。设操作前其他堆的Nim和为Z,则有Y xor Z = X。操作后的Nim和为X xor Y xor Z = X xor X = 0,是一个P状态。
二、每个P状态(必胜态)都不可以一步到达P状态
由于只能改变一堆的物品,不管修改它的哪一位,Nim的对应位一定不为0,不可能是P状态。

猜你喜欢

转载自www.cnblogs.com/qq1028152659/p/9316151.html