博弈论

博弈论

巴什博弈(Bash Game)

有一堆n个物品,两个人从轮流中取出(1~m)个;最后取光者胜。

考虑到 若n=m+1 那么 第一个人不论如何取都不能取胜。

进一步我们发现 若 n=k*(m+1)+r; 先取者拿走 r 个,那么后者再拿(1~m)个

n=(k-1)*(m+1)+s; 先取者再拿走s 个 最后总能造成 剩下n=m+1 的局面。

因此,此时先手有必赢策略。

相对应的,若n=k*(m+1) 那么先取者必输。

因此我们可以写出对应的程序(默认 n m都大于0)

int Bash_Game(int n,int m)//是否先手有必赢策略
{
    if (n%(m+1)!=0) return 1;
    return 0;
}

从巴什博弈我们知道一个当情形对应一种状态,而由一个状态只能变为另一种状态时能很轻易地判断是否先手有必赢策略

尼姆博弈(Nimm Game)

当有N堆,每堆有Mi>0个物品,依旧是两个人来取, 考虑取的最大数目无上限即可以把一堆全部取完的情形。

如果把n堆抽象为n个非负整数,再将n个整数转化为二进制,然后对n个二进制数按位相加(不进位),若每一位相加都为偶数,

那么称这个状态为偶状态,否则称它为奇状态.

可以证明:任何一个偶状态在其中一个数变小后一定成为奇状态,而一个奇状态一定可以通过改变一个数变成偶状态.

前一点很显然,因为一个数变小至少有一位发生改变,这一位就改变了原来的偶状态.
对于后一点,对于一个从高位到低位某一位和为奇的奇状态,必定有一个数的二进制表示在此位为1,对于后面的较低位和为奇的情况,只要把这个数对应位取反即可得到一个偶状态.

到此,成功的构造了两个可以转换的状态。那么对于n堆物品,只要判断它是否是奇状态就可以判断是否先手有必赢策略.

如果有奇数个二进制数在第K位为1 那么在这一位上的和为奇,同样的,偶数个1和为偶. 很明显位运算xor满足我们的要求,偶数个1异或和为0,奇数个为1.

int Nimm_Game(int n)//假设n个数存在数组f[]中,有必胜策略返回1
{
    int flag=0;
    for(int i=1;i<=n;i++)
    flag^=f[i];
    if(flag) return 1;
    return 0;
}

nimk游戏

如果将游戏改成每人每次可以从[1,d]堆中各取任意多个石子呢?

这个游戏实际是nim游戏的扩展版,也有一个很好的结论:将每堆石子数用二进制表示,对于二进制的任意一位,如果这一位为1的石子堆数 % ( d + 1 ) \%(d+1) %(d+1)都得0,那么先手必败,否则先手必胜。

证明的话与nim游戏类似,我们只需要证明三点:

  1. 终止局面为先手必败(显然)

  2. 任意先手必胜的局面都能传变成先手必败的局面。

  3. 任意先手必败的局面都不能转变为先手必胜的局面。

先来证明第2个:假设最高一位 % ( d + 1 ) \%(d+1) %(d+1)不得0的位有m个1,那么将这些堆的这一位变成0;假设下一个 % ( d + 1 ) \%(d+1) %(d+1)不得0的位有n个1,之前那m堆中这一位有a个1和b个0。如果 n ≤ a n≤a na,显然将这a个中n个变成0即可;如果 ( d + 1 ) − n ≤ b (d+1)−n≤b (d+1)nb,那么只要将b个中的 ( d + 1 ) − n (d+1)−n (d+1)n个变成1即可,因为之前最高位是将1变0,所以这一位即使由0变1,这堆石子数也是减少的;如果这两个都不满足,那么 a < n & & b < ( d + 1 ) − n a<n\&\&b<(d+1)−n a<n&&b<(d+1)n,这时只要将这a堆和这m堆之外的 n − a n−a na堆的这一位1变成0,那么总改变堆数为 a + b + ( n − a ) = b + n < ( d + 1 ) − n + n = d + 1 a+b+(n−a)=b+n<(d+1)−n+n=d+1 a+b+(na)=b+n<(d+1)n+n=d+1,也就是说将这一位变为 % ( d + 1 ) = 0 \%(d+1)=0 %(d+1)=0需要改变的总堆数要小于 ( d + 1 ) (d+1) (d+1),即可以一次操作完成,然后以此类推就能使每一位都变为 % ( d + 1 ) = 0 \%(d+1)=0 %(d+1)=0

再证明第3个:因为一次最多操作d堆石子,因此不能将(d+1)堆某一位是1的石子堆的这一位都变为0。

即各个位上1的个数mod(d+1)都为0则先手必败,否则先手必胜。

anti-nim

反nim游戏。正常的nim游戏是取走最后一颗的人获胜,而反nim游戏是取走最后一颗的人输。

一个状态为必胜态,当且仅当:

1)所有堆的石子个数为1,且NIM_sum=0

2)至少有一堆的石子个数大于1,且 NIM_sum≠0

求连续数字的异或和

f ( a , b ) f(a,b) f(a,b)表示 a − b a-b ab的异或和。
对于 k > = 1 k>=1 k>=1
f ( 2 k , 2 k + 1 − 1 ) f(2^k,2^{k+1}−1) f(2k,2k+11)中,最高位 2 k 2^k 2k的1出现了 2 k 2^k 2k次,异或和为0,所以最高位可以去掉
f ( 2 k , 2 k + 1 − 1 ) = f ( 2 k − 2 k , 2 k + 1 − 1 − 2 k ) = f ( 0 , 2 k − 1 ) f(2^k,2^{k+1}−1)=f(2^k−2^k,2^{k+1}−1−2^k)=f(0,2^{k−1}) f(2k,2k+11)=f(2k2k,2k+112k)=f(0,2k1)
所以 f ( 0 , 2 k + 1 − 1 ) = f ( 0 , 2 k − 1 ) X o r f ( 2 k , 2 k + 1 − 1 ) = 0 f(0,2^{k+1}−1)=f(0,2^{k−1})Xorf(2^k,2^{k+1}−1)=0 f(0,2k+11)=f(0,2k1)Xorf(2k,2k+11)=0
由此,对于所有 k > = 2 k>=2 k>=2,有 f ( 0 , 2 k − 1 ) = 0 f(0,2^k−1)=0 f(0,2k1)=0
所以,对于 f ( 0 , n ) f(0,n) f(0,n),设其最高位为 2 k 2^k 2k
那么 f ( 0 , n ) = f ( 0 , 2 k − 1 ) X o r f ( 2 k , n ) = f ( 2 k , n ) f(0,n)=f(0,2^k−1)Xorf(2^k,n)=f(2^k,n) f(0,n)=f(0,2k1)Xorf(2k,n)=f(2k,n)
最高位出现次数取决于n的奇偶。

  1. 若n为奇数,那么最高位出现了 n + 1 n+1 n+1次为偶数,即 f ( 2 k , n ) = f ( 0 , n − 2 k ) f(2^k,n)=f(0,n-2^k) f(2k,n)=f(0,n2k),又 n − 2 k n-2^k n2k与n同奇偶,所以可以一直递推,最后 f ( 0 , n ) f(0,n) f(0,n)就只取决于小于4的部分,即 m o d 4 mod4 mod4后的异或和。
    得到:若 n ≡ 1 ( m o d   4 ) , f ( 0 , n ) = 1 n\equiv 1(mod \ 4),f(0,n)=1 n1(mod 4),f(0,n)=1,若 n ≡ 3 ( m o d   4 ) , f ( 0 , n ) = 0 n\equiv 3(mod \ 4),f(0,n)=0 n3(mod 4),f(0,n)=0

  2. 若n为偶数,那么最高位出现了 n + 1 n+1 n+1次为奇数,最高位比克保留,即 f ( 2 k , n ) = 2 k + f ( 0 , n − 2 k ) f(2^k,n)=2^k+f(0,n-2^k) f(2k,n)=2k+f(0,n2k),又 n − 2 k n-2^k n2k与n同奇偶,所以可以一直递推,最后 f ( 0 , n ) f(0,n) f(0,n)就只取决于小于4的部分,即 m o d 4 mod4 mod4后的异或和
    得到:若 n ≡ 0 ( m o d   4 ) , f ( 0 , n ) = n n\equiv 0(mod \ 4),f(0,n)=n n0(mod 4),f(0,n)=n,若 n ≡ 2 ( m o d   4 ) , f ( 0 , n ) = n + 1 n\equiv 2(mod \ 4),f(0,n)=n+1 n2(mod 4),f(0,n)=n+1

综述:
n ≡ 0 ( m o d   4 ) , f ( 0 , n ) = n n\equiv 0(mod \ 4),f(0,n)=n n0(mod 4),f(0,n)=n
n ≡ 1 ( m o d   4 ) , f ( 0 , n ) = 1 n\equiv 1(mod \ 4),f(0,n)=1 n1(mod 4),f(0,n)=1
n ≡ 2 ( m o d   4 ) , f ( 0 , n ) = n + 1 n\equiv 2(mod \ 4),f(0,n)=n+1 n2(mod 4),f(0,n)=n+1
n ≡ 3 ( m o d   4 ) , f ( 0 , n ) = 0 n\equiv 3(mod \ 4),f(0,n)=0 n3(mod 4),f(0,n)=0

威佐夫博奕(Wythoff Game)

有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

( a k , b k ) ( a k ≤ b k , k = 0 , 1 , 2 , … , n ) (a_k,b_k)(a_k ≤ b_k ,k=0,1,2,…,n) (akbk)(akbk,k=012,n)表示
两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们
称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,
10)、(8,13)、(9,15)、(11,18)、(12,20)。

可以看出, a 0 = b 0 = 0 a_0=b_0=0 a0=b0=0, a k a_k ak是未在前面出现过的最小自然数,而$ b_k= a_k + k$,奇异局势有如下三条性质:

  1. 任何自然数都包含在一个且仅有一个奇异局势中。
    由于 a k a_k ak是未在前面出现过的最小自然数,所以有 a k > a k − 1 a_k > a_{k-1} ak>ak1 ,而 b k = a k + k > a k − 1 + k − 1 = b k − 1 > a k − 1 b_k= a_k + k > a_{k-1} + k-1 = b_{k-1} > a_{k-1} bk=ak+k>ak1+k1=bk1>ak1 。所以性质1成立。
  2. 任意操作都可将奇异局势变为非奇异局势。
    事实上,若只改变奇异局势 ( a k , b k ) (a_k,b_k) (akbk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使 ( a k , b k ) (a_k,b_k) (akbk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
  3. 采用适当的方法,可以将非奇异局势变为奇异局势。
    假设面对的局势是 ( a , b ) (a,b) (a,b),若 b = a b = a b=a,则同时从两堆中取走 a a a 个物体,就变为了奇异局势(0,0);如果 a = a k , b > b k a = a_k ,b > b_k a=akb>bk,那么,取走 b – b k b – b_k bbk个物体,即变为奇异局势;如果 a = a k , b < b k a = a_k , b < b_k a=akb<bk ,则同时从两堆中拿走 a – a b – a a–a_{b – a} aaba个物体,变为奇异局势 ( a b – a , b − a – a b − a ) (a_{b – a} , b -a– a_{b-a}) (aba,baaba);如果 a > a k , b = a k + k a > a_k ,b= a_k + k a>akb=ak+k,则从第一堆中拿走多余的数量 a – a k a – ak aak 即可;如果 a < a k , b = a k + k a < a_k ,b= a_k + k a<akb=ak+k,分两种情况,第一种, a = a j ( j < k ) a=a_j (j < k) a=aj(j<k),从第二堆里面拿走 b – b j b – b_j bbj 即可;第二种, a = b j ( j < k ) a=b_j (j < k) a=bjj<k,从第二堆里面拿走$ b – a_j$ 即可。

从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。

那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:

a k = ⌊ 1 + √ 5 2 k ⌋ , b k = a k + k a_k =\lfloor \frac {1+√5}{2}k\rfloor,b_k= a_k + k ak=21+5kbk=ak+k (k=0,1,2,…,n )

int a, b;
    while (scanf("%d%d", &a, &b) != EOF)
    {
        if (a > b) swap(a, b);
       int temp = abs(a - b);
        int ans = temp * (1.0 + sqrt(5.0)) / 2.0;
        if (ans == a) printf("0\n");
        else 	   printf("1\n");
    }

EX:扩展威佐夫博弈

按照在棋盘上移动棋子的方式,每次向左下移动可以看作是向左移动 d x dx dx,向下移动 d y dy dy,要求 ∣ d x − d y ∣ < 1 |dx−dy|<1 dxdy<1。那么如果将这个要求扩展到 ∣ d x − d y ∣ < d |dx−dy|<d dxdy<d呢?我们同样可以得出第k个奇异局势(x,y),x为前 0 ∼ k − 1 0∼k−1 0k1个奇异局势中没有出现过的最小自然数, y = x + d ∗ k y=x+d∗k y=x+dk。根据Betty定理同样能得出第k个奇异局势为 ( ⌊ 2 − d + d 2 + 4 2 k ⌋ , ⌊ 2 + d + d 2 + 4 2 k ⌋ ) (\lfloor\frac{2−d+\sqrt{d^2+4}}{2}k\rfloor,\lfloor\frac{2+d+\sqrt{d^2+4}}{2}k\rfloor) (22d+d2+4 k,22+d+d2+4 k)

SG函数和SG定理

SG函数是用于解决博弈论公平组合游戏Impartial Combinatorial Games,ICG)问题的一种方法。

所谓ICG,应当满足以下几条性质:

  1. 有两名玩家
  2. 两名玩家轮流操作,在一个有限集合内任选一个进行操作,改变游戏当前局面
  3. 一个局面的合法操作,只取决于游戏局面本身且固定存在,与玩家次序或者任何其它因素无关
  4. 无法操作者,即操作集合为空,输掉游戏,另一方获胜

对于ICG,我们定义一个函数 s g ( x ) sg(x) sg(x) ,令 s g ( x ) = m e x ( s g ( y ) ∣ x → y ) sg(x)=mex({sg(y)|x\rightarrow y}) sg(x)=mex(sg(y)xy)。其中, x , y x,y x,y都表示某种状态 x → y x\rightarrow y xy在这里表示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ykVWdkjz-1613634983545)(https://www.zhihu.com/equation?tex=x)] 状态可以通过一次操作达到 y y y状态, m e x mex mex表示一个集合中未出现的最小自然数(例如 m e x ( 0 , 1 , 3 ) = 2 mex({0,1,3})=2 mex(0,1,3)=2。如果 s g ( x ) = n sg(x)=n sg(x)=n,说明从当前状态可以转移到 s g sg sg 0 , 1 , 2 , 3 , . . , n − 1 0,1,2,3,..,n-1 0,1,2,3,..,n1的状态。

我们称令 s g ( x ) = 0 sg(x)=0 sg(x)=0的状态 x x x必败态。显然, x x x如果是无法操作的状态(即 { s g ( y ) ∣ x → y } = ∅ \{sg(y)|x\rightarrow y\}=\varnothing {sg(y)xy}=,在Nim游戏中是没有石子可取的状态),则必有 s g ( x ) = 0 sg(x)=0 sg(x)=0,这时当前玩家输掉游戏;若不然,则该状态只能转移到 s g sg sg不为 0的状态,那么对手立刻又可以转移到 s g sg sg为 0的状态,这样进行有限次后一定会陷入无法操作的状态输掉比赛。

相反,如果 s g ( x ) ≠ 0 sg(x)\neq0 sg(x)=0,则称 x x x必胜态,说明此时存在策略使自己必定取胜(即每次轮到自己都转移到 s g sg sg为0的状态)。

当ICG中存在 n n n个互相不干扰的移动类型时,我们可以将这 n n n种移动类型视为 n n n堆石子,将该ICG视为 n n n堆石子的Nim游戏。运用前面的Bouton定理,该ICG的先手必胜与否的情况可以通过计算每个移动类型下的初始状态的SG函数,并计算这些SG函数值的异或和来得出。

//解题不用set,易超时
int mex(auto v) // v可以是vector、set等容器 
{
    unordered_set<int> S;
    for (auto e : v)
        S.insert(e);
    for (int i = 0;; ++i)
        if (S.find(i) == S.end())
            return i;
}
//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
int f[N],SG[MAXN],S[MAXN];
void  getSG(int n){
    int i,j;
    memset(SG,0,sizeof(SG));
    //因为SG[0]始终等于0,所以i从1开始
    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;  //将后继状态的SG函数值进行标记
        for(j = 0;; j++) if(!S[j]){   //查询当前后继状态SG值中最小的非零值
            SG[i] = j;
            break;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值