【转】博弈论--取物品和Nim游戏

感慨数学归纳法真强大!

郗博的文章写得真好,情不自禁的就转过来了。

PS:今天晚上在实验室研究这些东西的时候用手抓到两只蚊子,并且好像都残了,前进的路上总是沾满了XX。。。

分为三个部分,第一部分是简单版的甲乙两人取物品,第二部分是博弈论中经典的模型--Nim游戏,最后是关于Nim游戏的介绍和Bouton's Theorem。


第一部分:简单版的甲乙两人取物品

前序
先来看一个更常见的取物品的题目:一堆n个物品,甲乙两人轮流取,每次取1至m个,最后取完者胜。
分析:
1.1-m个物品时,甲稳赢(当然要保证俩人都足够聪明,也就是说甲没傻到当有2个物品时,他却只取一个)
2.m+1个物品时,乙稳赢。此时无论甲取多少个(1-m),剩下的、乙总能够一次性取完
3.m+2个时,甲稳赢。此时甲拿一个,而无论乙拿几个都会输。其实这时候当乙开始拿物品时,它相当于遇到了第二步中甲所面临的情况(也就是当前物品只剩m+1个,无论自己怎么取,对方都会赢)
4.m+3——2m+1个时,甲稳赢。此时甲只要取2——m个,就会让乙面临m+1这种必输的情况
5.2m+2个时,乙稳赢。此时无论甲取多少个(假设为x),2m+2-x都会落在m+2到2m+1这个区间,此时的乙就面临第四步的情况,所以乙稳操胜券。
6.数学归纳法。我们假设k(m+1)个时,乙稳赢,k(m+1)+1——k(m+1)+m时,甲稳赢,则(k+1)(m+1)时,甲取y个,此时乙面临的个数是(k+1)(m+1)-y==k*(m+1)+m+1-y,介于k(m+1)+1——k(m+1)+m之间,根据假设,也就是说此时乙稳赢。同理,可得当个数在(k+1)(m+1)+1——(k+1)(m+1)+m时,甲稳赢。
前序小结
那么肿么才能让甲稳赢呢?如果n%(m+1)==0,那甲你就别想了,你赢不了了;其他情况,恭喜你,甲赢了,甲的策略就是每次取完都给乙剩下k(m+1)个。上面没提n==0的情况,这个很显然也符合,忘掉了。。。
问题
俩聪明鬼A、B在数盒子里的星星,俩人每人一次只能拿23-28个星星,当某一次某人想拿的时候不够这个数了,就算输。
分析
此题目和上面的很类似,只不过区间变了。
1.0-22个时,A肯定输,因为去不了23——28这么多
2.23-27时,A稳赢,因为可以随便一手抓掉。
3.28-50时,A稳赢,此时A只要取28个,B就面临A在第一步时的处境了
4.51-73时,B稳赢,因为不管A抓多少个(当然,必须是在23——28之间),此时剩下的数肯定在23——50之间,也就是说B面临了A在二、三步的情况,所以B稳赢
5.74-101时,A稳赢,分两种情况:74-96时,A先抓23个,此时剩下51-73,B面临A在第四步的情形,所以A稳赢;97-101时,甲取28个,这样剩69——73个,仍是A赢
6.。。。和前序一样数学归纳法,可以证得51*k——51*k+22时,B稳赢,其它情况A稳赢。
总结规律:
根据上面两个例子,我们可以大胆假设,甲、乙取物品,每次只能取a——b之间的数量,一共sum个物品,则甲稳输的情况位于(a+b)*k到(a+b)*k+(a-1)之间
证明
第一步:当a==b时,显然,最简单了,只要求ans = sum/a,看a是否为偶数,偶数就会输,也就是sum/a%2为0时,甲就会输。公式也就变成了2a*k——2a*k+a-1之间,除以a,商为2*k,2*k%2==0,所以满足公式。
第二步:a==1,b==m时,这也就是前言中的情况,带入公式得到(1+m)*k——(1+m)*k+0,也就是前言中推导出来的(1+m)*k时,乙稳赢,所以满足公式。
第三步:假设i——j的时候满足,也就是说当物品个数位于(i+j)*k到(i+j)*k+(i-1)时,乙稳赢,其他情况,甲稳赢。
则当a==i,b==j+1时:
1.0——i-1时,乙稳赢。
2.i——j时,甲稳赢
3.j+1——i+j时,此时甲取j+1个,此时剩下0到i-1个物品,此时乙面临甲在第一步时的败局,所以甲稳赢。
4.同理,数学归纳法,可证。
当a==i+1,b==j时,同理,也可证。
结论
甲、乙取物品,每次只能取a——b之间的数量,则甲稳输的情况时总物品的数量在(a+b)*k到(a+b)*k+(a-1)之间,其它数量的时候甲稳赢,所以,甲每次需要做的就是取适量的物品,使得乙面对的物品在(a+b)*k到(a+b)*k+(a-1)之间。


第二部分:博弈论中经典的模型--Nim游戏例子

先来个POJ的例子:POJ-2234:Matches Game

时间限制: 
 1000ms 
 内存限制: 
 65536kB
描述
Here is a simple game. In this game, there are several piles of matches and two players. The two player play in turn. In each turn, one can choose a pile and take away arbitrary number of matches from the pile (Of course the number of matches, which is taken away, cannot be zero and cannot be larger than the number of matches in the chosen pile). If after a player’s turn, there is no match left, the player is the winner. Suppose that the two players are all very clear. Your job is to tell whether the player who plays first can win the game or not.
输入
The input consists of several lines, and in each line there is a test case. At the beginning of a line, there is an integer M (1 <= M <=20), which is the number of piles. Then comes M positive integers, which are not larger than 10000000. These M integers represent the number of matches in each pile.
输出
For each test case, output "Yes" in a single line, if the player who play first will win, otherwise output "No".
样例输入
2 45 45

3 3 6 9
样例输出
No

Yes


(一)这是一道博弈论Nim Game的题目,我们先从简单情况开始分析,再逐步深入。

(1)只有一堆,先手赢
(2)两堆,一共2根火柴,后手赢              (取完一堆即可)
  两堆,一共3根火柴,先手赢               (1+2格局,在2个的一堆中取1个,转化为前一种情形)
  两堆,一堆2个,另一堆2个,后手赢 (取完一堆,或者取1个都必输)
  两堆,一堆1个,另一堆3个,先手赢 (1+3格局,在3个的一堆中取2个,转化为2根的情形)
          ...   
  两堆,堆中火柴数目相等,后手赢
  两堆,堆中火柴数目不等,先手赢  
(3)三堆,一共3根火柴,先手赢 
  三堆,一共4根火柴,先手赢
  三堆,一共5根火柴,1.1.3型先手赢   (因为总能够转化到两堆相等的情况)
  三堆,一共5根火柴,2.2.1型先手赢   (因为总能够转化到两堆相等的情况)
  三堆,一共6根火柴,2.2.2型先手赢 
  三堆,一共6根火柴,1.1.4型先手赢
  三堆,一共6根火柴,1.2.3型后手赢 

通过简单的分析发现规律并不易寻找,并不是简单的奇偶关系(我就错误地推出过奇偶胜负关系)

(二)我们开始深入一点

    我们发现在两堆时,我们找到一个有趣的规律,那就是如果两堆数目相等,那么后手方赢,如果两堆数目不等,先手方赢(思考一下为什么?)。但三堆的时候貌似又行不通了,因为如最简单的1+1+1格局,显然是各堆数目相等,但应该是先手方赢。但我们马上会发现,对于三堆的情形,如果各堆数目相等,一定是先手方赢(想清楚为什么?)。进一步推广,我们可以得到如下结论:

堆为奇数个,各堆中火柴数目相等,一定是先手方赢

堆为偶数个,各堆中火柴数目相等,一定是后手方赢

(三)我们开始处理最棘手的部分

我们现在能够处理,所有堆中火柴都相等的时候的情形,但各堆中火柴不相等呢?这里我们再次看到数学中一些不可思议的联系。我们现在重新审视和定义这个游戏的胜负

(1)如果轮到某人取火柴的时候,火柴已经没有了,那么此人输,设为P-格局

(2)如果轮到某人取火柴的时候,他能够取完火柴,那么此人赢,设为N-格局

(3)如果轮到某人取火柴的时候,他能够将当前格局转化为某个P格局,那么此格局为N-格局

(4)如果轮到某人取火柴的时候,他不能将当前格局转化为某个P格局,那么此格局为P-格局

下面我们来推导一个定理:一个格局记为(x1,x2,...,xn),且次序无关,此格局为 P-格局 当且仅当 x1^x2^...^xn = 0.其中^是按位异或运算

推导:  对应上述4种情形(为了叙述的简洁,并不十分严谨地证明,而是直接假设结论正确,再说明定理的工作机制)

            (1)当(x1,x2,...,xn)中全为0时            ,格局为P-格局,此时x1 ^ x2 ^ ... ^ xn = 0成立。

            (2)当(x1,x2,...,xn)中只有一个不为0,格局为N-格局,此时x1 ^ x2 ^ ... ^ xn = 0不成立。

            (3)当(x1,x2,...,xn)是P-格局时,x1,...,xn不全为0.(反证法)

                                          假设x1 ^ x2 ^ ... ^ xn = p,且p不为0,

                                          记 p 的二进制表示中,最左的1(最高位的1)在从左至右数第 k 位.

                                          由于p是异或运算的结果,那么 x1, x2 , ... , xn中至少有一个数第k位为1,

                                          不妨设 xi 的第 k 位为1,那么 xi ^ p 第 k 位为0,那么xi > xi^p 显然成立.

                                          也就是说,存在某种取法,使i堆的火柴数变化到 xi^p .

                                          题设x1 ^ x2 ^ ... ^ xn = p,那么x1 ^ x2 ^ ... ^ xn ^ p = 0.

                                          那么当前格局可以转化到某个P-格局,也就是说当前格局时N-格局,矛盾

                                          所以,必有p=0.

                (4)当x1 ^ x2 ^ ... ^ xn = 0时,如果存在某个移动 xi 变化到 xi ’ ,且x1^x2^....^xi ' ^...^xn = 0,那么由异或运算的消去律有,xi = xi ' ,也就是说一根火柴都没取,这不允许的,所以当前格局只能是P格局

有了这个强大的定理那么上题就很好解决了:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int main()  
  5. {  
  6.   
  7.     int M;  
  8.     while(cin>>M)  
  9.     {  
  10.         /*input*/  
  11.         int pile[20] ={};  
  12.         for(int i=0; i<M; ++i)  
  13.         {   cin>>pile[i]; }  
  14.   
  15.         /*calculate*/  
  16.         int result   = 0;  
  17.         for(int i=0; i<M; ++i)  
  18.         {   result ^= pile[i];  }  
  19.   
  20.         /*output*/  
  21.         if(result)cout<<"Yes"<<endl;  
  22.         else      cout<<"No" <<endl;  
  23.     }  
  24.   
  25.     return 0;  
  26. }  

简单来说,若干个数的异或和为非0,我们总能只通过减小某个数的大小,从而使异或和为0。我们只要减小和异或和最高位所在的位置都是1的那个数即可。比如3个数,0101,0100,0010(都是二进制的),异或是0011,那么只要减小0010即可,而且这个数总能找到,因为必定有某个数在那个位置贡献了1。这样我们把那个数的对应异或和里是1的位置1变0,0变1即可,对于刚才的例子就是把0010变成0001。有了这个事实,回到nim游戏,先手总可以把石子的异或和变为0,而后手无论怎么做都只能把异或和变为非0,这样先手一定可以达到异或和是0的没有石子的状态,也就是赢了这个游戏。

ok,扩展一下,如果每次不是选择一堆石子而是选择两堆石子去取呢,这个时候就是考虑三进制的异或和了(这里有点小trick,就是数字仍然是变为2进制,但是异或操作是3进制的异或操作)。也就是说只要异或和不是0,总可以通过减少两个数使得异或和是0,而且,只要异或和是0,不管怎么减少任意两堆石子,异或和都会变为非0。如果还是二进制的话,后手显然可以通过改变两堆石子使得异或仍为0,这样就不能保证先手必胜了。

让我们再看一个进位制妙用的例子。记得有一个有趣的题是这样的,给定若干个数,其中只有一个数出现了1次,其他的都出现了2次,如何只用额外的常数空间找到这个数呢。熟悉这道题的一般一眼就看出答案了吧,就是把所有数异或,那么就可以找到这个数了。

进一步的,如果只有一个数只出现了1次,其他的出现了3次呢,相信看过本文的童鞋们一定可以想到,这次只要利用三进制的异或和就可以轻松解决了。当然还有其他扩展如有两个数出现1次,其他的出现2次,或者有一个数出现2次,其他的出现3次啦,这里就不再赘述了。

总之有时候利用进位制的一些特性,帮我们解决题目的时候会有意想不到的结果呢。



第三部分:Nim游戏介绍

Nim游戏是博弈论中最经典的模型(之一),它又有着十分简单的规则和无比优美的结论 Nim游戏是组合游戏(Combinatorial Games)的一种,准确来说,属于“Impartial Combinatorial Games”(以下简称ICG)。

条件

满足以下条件的游戏是ICG(可能不太严谨):1、有两名选手;2、两名选手交替对游戏进行移动(move),每次一步,选手可以在(一般而言)有限的合法移动集合中任选一种进行移动;3、对于游戏的任何一种可能的局面,合法的移动集合只取决于这个局面本身,不取决于轮到哪名选手操作、以前的任何操作、骰子的点数或者其它什么因素; 4、如果轮到某名选手移动,且这个局面的合法的移动集合为空(也就是说此时无法进行移动),则这名选手负。根据这个定义,很多日常的游戏并非ICG。例如象棋就不满足条件3,因为红方只能移动红子,黑方只能移动黑子,合法的移动集合取决于轮到哪名选手操作。

定义

通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
这游戏看上去有点复杂,先从简单情况开始研究吧。如果轮到你的时候,只剩下一堆石子,那么此时的必胜策略肯定是把这堆石子全部拿完一颗也不给对手剩,然后对手就输了。如果剩下两堆不相等的石子,必胜策略是通过取多的一堆的石子将两堆石子变得相等,以后如果对手在某一堆里拿若干颗,你就可以在另一堆中拿同样多的颗数,直至胜利。如果你面对的是两堆相等的石子,那么此时你是没有任何必胜策略的,反而对手可以遵循上面的策略保证必胜。如果是三堆石子……好像已经很难分析了,看来我们必须要借助一些其它好用的(最好是程式化的)分析方法了,或者说,我们最好能够设计出一种在有必胜策略时就能找到必胜策略的算法。
定义P-position和N-position,其中P代表Previous,N代表Next。直观的说,上一次move的人有必胜策略的局面是P-position,也就是“后手可保证必胜”或者“先手必败”,现在轮到move的人有必胜策略的局面是N-position,也就是“先手可保证必胜”。更严谨的定义是:1.无法进行任何移动的局面(也就是terminal position)是P-position;2.可以移动到P-position的局面是N-position;3.所有移动都导致N-position的局面是P-position。
按照这个定义,如果局面不可能重现,或者说positions的集合可以进行拓扑排序,那么每个position或者是P-position或者是N-position,而且可以通过定义计算出来。

计算

以Nim游戏为例来进行一下计算。比如说我刚才说当只有两堆石子且两堆石子数量相等时后手有必胜策略,也就是这是一个P-position,下面我们依靠定义证明一下(3,3)是一个P-position。首先(3,3)的子局面(也就是通过合法移动可以导致的局面)有(0,3)(1,3)(2,3)(显然交换石子堆的位置不影响其性质,所以把(x,y)和(y,x)看成同一种局面),只需要计算出这三种局面的性质就可以了。 (0,3)的子局面有(0,0)、(0,1)、(0,2),其中(0,0)显然是P-position,所以(0,3)是N-position(只要找到一个是P-position的子局面就能说明是N-position)。(1,3)的后继中(1,1)是P-position(因为(1,1)的唯一子局面(0,1)是N-position),所以(1,3)也是N-position。同样可以证明(2,3)是N-position。所以(3,3)的所有子局面都是N-position,它就是P-position。通过一点简单的数学归纳,可以严格的证明“有两堆石子时的局面是P-position当且仅当这两堆石子的数目相等”。
根据上面这个过程,可以得到一个递归的算法——对于当前的局面,递归计算它的所有子局面的性质,如果存在某个子局面是P-position,那么向这个子局面的移动就是必胜策略。当然,可能你已经敏锐地看出有大量的重叠子问题,所以可以用DP或者记忆化搜索的方法以提高效率。但问题是,利用这个算法,对于某个Nim游戏的局面(a1,a2,...,an)来说,要想判断它的性质以及找出必胜策略,需要计算O(a1*a2*...*an)个局面的性质,不管怎样记忆化都无法降低这个时间复杂度。所以我们需要更高效的判断Nim游戏的局面的性质的方法。

结论

(Bouton's Theorem)对于一个Nim游戏的局面(a1,a2,...,an),它是P-position当且仅当a1^a2^...^an=0,其中^表示异或(xor)运算。怎么样,是不是很神奇?我看到它的时候也觉得很神奇,完全没有道理的和异或运算扯上了关系。但这个定理的证明却也不复杂,基本上就是按照两种position的证明来的。

证明

根据定义,证明一种判断position的性质的方法的正确性,只需证明三个命题: 1、这个判断将所有terminal position判为P-position;2、根据这个判断被判为N-position的局面一定可以移动到某个P-position;3、根据这个判断被判为P-position的局面无法移动到某个P-position。
第一个命题显然,terminal position只有一个,就是全0,异或仍然是0。
第二个命题,对于某个局面(a1,a2,...,an),若a1^a2^...^an<>0,一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。不妨设a1^a2^...^an=k,则一定存在某个ai,它的二进制表示在k的最高位上是1(否则k的最高位那个1是怎么得到的)。这时ai^k<ai一定成立。则我们可以将ai改变成ai'=ai^k,此时a1^a2^...^ai'^...^an=a1^a2^...^an^k=0。
第三个命题,对于某个局面(a1,a2,...,an),若a1^a2^...^an=0,一定不存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。因为异或运算满足消去率,由a1^a2^...^an=a1^a2^...^ai'^...^an可以得到ai=ai'。所以将ai改变成ai'不是一个合法的移动。证毕。
根据这个定理,我们可以在O(n)的时间内判断一个Nim的局面的性质,且如果它是N-position,也可以在O(n)的时间内找到所有的必胜策略。Nim问题就这样基本上完美的解决了。




参考文章:

http://tristan-xi.org/?p=250

http://tristan-xi.org/?p=266

http://blog.csdn.net/theprinceofelf/article/details/7221775

http://www.isnowfy.com/nim-game-and-numeral-system/

http://baike.baidu.com/view/1101962.htm


你可能感兴趣的:(Theory,game,game,博弈论,Nim)