取石子游戏详解NIM

 

取石子游戏详解NIM

分类: 编程之美   478人阅读  评论(3)  收藏  举报

目录(?)[+]html

http://blog.csdn.net/pipisorry/article/details/39249337
java

取石子游戏详解NIMweb

取石子游戏详解NIM算法

取石子游戏详解NIM编程

取石子游戏详解NIMide

取石子游戏详解NIM函数


取石子游戏是一个古老的博弈游戏,发源于中国,它是组合数学领域的一个经典问题。它有许多不一样的玩法,基本上是两个玩家,玩的形式是轮流抓石子,胜利的标准是抓走了最后的石子。post


玩家设定: 先取石子的是玩家A,后取石子的是玩家B。spa


经典的三种玩法:

1、巴什博奕(Bash Game),有1堆含n个石子,两我的轮流从这堆物品中取物,规定每次至少取1个,最多取m个。取走最后石子的人获胜。.net

2、尼姆博奕(Nimm Game),有k堆各n个石子,两我的轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限。取走最后石子的人获胜。

3、威佐夫博奕(Wythoff Game),有2堆各n个石子,两我的轮流从某一堆或同时从两堆中取一样多的物品,规定每次至少取1个,多者不限。取走最后石子的人获胜。POJ1067


平衡状态的概念:

   引入一个概念,平衡状态,又称做奇异局势。当面对这个局势时则会失败。任意非平衡态通过一次操做能够变为平衡态。每一个玩家都会努力使本身抓完石子以后的局势为平衡,将这个平衡局势留给对方。所以,玩家A可以在初始为非平衡的游戏中取胜,玩家B可以在初始为平衡的游戏中取胜。


玩法一(1堆n个石子每次最多取m个):

显然,若是n=m+1,那么因为一次最多只能取m个,因此,不管先取者拿走多少个,后取者都可以一次拿走剩余的物品,后者取胜。所以咱们发现了如何取胜的法则:若是n=(m+1)r+s,(r为任意天然数,s≤m),那么先取者要拿走s个物品,若是后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,之后保持这样的取法,那么先取者确定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。

即,若n=k*(m+1),则后取着胜,反之,存在先取者获胜的取法。n%(m+1)==0. 先取者必败

    这个游戏还能够有一种变相的玩法:两我的轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。(<=>从一堆100个石子中取石子,最后取完的胜

最后一个奇异局势是n=(0)。一种奇异局势是,n=(m+1),那么不管我取走多少个,对方都可以一次取走剩余全部的物品取胜。

奇异局势的断定:

    通常的奇异局势是n=(m+1)*i,其中i为天然数,即n%(m+1)=0,面对这种状况不管我怎么取,对方总能够将其恢复为n%(m+1)=0,一直到n=(m+1)局势。

玩家的策略:

    就是把当前面对的非奇异局势变为奇异局势留给对方。若是当前的石子个数为(m+1)*i+s,那么就将s个石子取走,使其达到奇异局势。


玩法二(k堆石子每次只从1堆取):

    简化问题有三堆各若干个物品,两我的轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

    这种状况最有意思,它与二进制有密切关系,咱们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,不管谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走同样多的物品,最后都将致使(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,不管对手如何拿,接下来均可以变为(0,n,n)的情形。
    计算机算法里面有一种叫作按位模2加,也叫作异或的运算,咱们用符号(+)表示这种运算。这种运算和通常加法不一样的一点是1+1=0。先看(1,2,3)的按位模2加的结果:
1 =二进制01
2 =二进制10
3 =二进制11 (+)
———————
0 =二进制00 (注意不进位)
    对于奇异局势(0,n,n)也同样,结果也是0。
    任何奇异局势(a,b,c)都有a(+)b(+)c =0。
若是咱们面对的是一个非奇异局势(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)便可。

获胜状况对先取者的讨论:

异或结果为0,先取者必败,无获胜方法。后取者获胜;

结果不为0,先取者有获胜的取法。

 拓展: 任给N堆石子,两人轮流从任一堆中任取(每次只能取自一堆),取最后一颗石子的人获胜,问先取的人如何获胜?

根据上面所述,N个数异或便可。若是开始的时候T=0,那么先取者必败,若是开始的时候T>0,那么只要每次取出石子使得T=0,即先取者有获胜的方法。

最后一个奇异局势是(0,0...,0)。另外一个奇异局势是(n,n,0...0),只要对手老是和我拿走同样多的物品,最后会面对(0,0...,0)。

奇异局势的断定:

    对于一个普通的局势,如何判断其是否是奇异局势?对于一个局势(s1,s2,...sk),对全部石子个数作位的异或运算,s1^s2^s3^...^sk,若是结果为0,那么局势(s1,s2,...sk)就是奇异局势(平衡),不然就不是(非平衡)。

    从二进制位的角度上说,奇异局势时,每个bit位上1的个数都是偶数。

玩家的策略:

    就是把面对的非奇异局势变为奇异局势留给对方。也就是从某一堆取出若干石子以后,使得每个bit位上1的个数都变为偶数,这样的取法通常不仅有一种。能够将其中一堆的石子数变为其余堆石子数的位异或运算的值(若是这个值比原来的石子数小的话)。


玩法三(2堆石子每次从一或两堆取同样数目的石子):

    前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。。。

奇异局势的断定:

能够看出,a0=b0=0,ak是未在前面出现过的最小天然数,而 bk= ak + k,奇异局势有以下三条性质:
    一、任何天然数都包含在一个且仅有一个奇异局势中。
    因为ak是未在前面出现过的最小天然数,因此有ak > ak-1 ,而 bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 。因此性质1。成立。
    二、任意操做均可将奇异局势变为非奇异局势。
    事实上,若只改变奇异局势(ak,bk)的某一个份量,那么另外一个份量不可能在其余奇异局势中,因此必然是非奇异局势。若是使(ak,bk)的两个份量同时减小,则因为其差不变,且不多是其余奇异局势的差,所以也是非奇异局势。
    三、采用适当的方法,能够将非奇异局势变为奇异局势。假设面对的局势是(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 - aj 便可。

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

    那么任给一个局势(a,b),怎样判断它是否是奇异局势呢?

    这个奇异局势的序列的通项公式能够表示为:
    

  Ak = [k*(1+sqrt(5.0)/2]

  Bk = Ak + k  

  其中k=0,1,2,...,n ,方括号表示int取整函数。

    有了这个通项式子,逆向的,对于某一个局势,只须要判断其A是不是黄金分割数的某个k的倍数,而后再确认B是否等于A+k便可。


奇妙的是其中出现了黄金分割数(1+√5)/2 = 1.618...,所以,由ak,bk组成的矩形近似为黄金矩形,因为2/(1+√5)=(√5-1)/2,能够先求出j=[a(√5-1)/2],若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,若都不是,那么就不是奇异局势。而后再按照上述法则进行,必定会遇到奇异局势。    

咱们会发现这个序列的规律,设序列第k个奇异局势元素为(Ak,Bk),k为天然数。那么,初始条件k=0时是,A0=B0=0,递推关系为下一个奇异局势的Ak是未在前面出现过的最小天然数,且Ak = Bk + k。

变种玩法:“皇后爬山”游戏,在空的围棋棋盘上放一个棋子,该棋子每次只能向上或向右或沿对角线向右上方向移动(类似国际象棋),能够移动任意格,但不能不移动,两人轮流移动棋子,先将棋子移动到右上角者赢,问先移棋者的必胜策略。《智力游戏中的数学方法》

code:

[cpp]  view plain copy 在CODE上查看代码片 派生到个人代码片
  1. /****************************************************************************/  
  2. /*      编程之美 - NIM捡石子问题 皮皮 2014-9-10        */  
  3. /****************************************************************************/  
  4. #include <assert.h>  
  5. #include <stdio.h>  
  6. #include <math.h>  
  7.   
  8. /*  黄金分割数最优算法   */  
  9. static int nim(int x, int y){  
  10.     double a = (sqrt(5.0) + 1) / 2;             //goldenNum 1.618...  
  11.     int n = abs(x - y);                         //bn - an  
  12.     x = x < y? x : y;                            //取小者  
  13.     return ( x != int(a * n) );  
  14. }  
  15.   
  16. int main(){  
  17.     int x, y;  
  18.     while(scanf("%d%d", &x, &y) == 2)  
  19.         printf("%d\n", nim(x, y));  
  20.     return 0;  
  21. }  



拓展:?

 【综合1、三】

任给N堆石子,两人轮流从任一堆中任取(每次只能取自一堆),规定每方每次最多取K颗,取最后一颗石子的一方获胜.问先取的人如何获胜?

与上面的问题比,这个更复杂一些,咱们能够这样作

令Bi=Ai mod(K+1)

定义T‘=B1 xor B2 xor ... xor Bn

若是T‘=0 那么没有获胜可能,先取者必败

若是T’>0 那么必然存在取的方法,使得T‘=0,先取者有获胜的方法

假设对方取了在Ai中取了r<=K个

若是Ai中剩下的石子多于K 那么就在Ai中取走K+1-r个则Bi不变 T‘仍是0

若是Ai<=K 那么咱们须要从新计算Bi和T‘ 按照上面的方法来作就能够了

【其它相似题型】

POJ1740 A New Stone Game
MIPT 100 Nim Game -- who is the winner? 
POJ 1704 Georgia and Bob




附:

1.Wythoff’s Game (威佐夫博弈)解释[相似解释亦可见于编程之美NIM(3)]

Wythoff's Nim

有两堆石子,不妨先认为一堆有10,另外一堆有15个,双方轮流取走一些石子,合法的取法有以下两种:

1)在一堆石子中取走任意多颗;

2)在两堆石子中取走相同多的任意颗;

约定取走最后一颗石子的人为赢家,求必败态(必胜策略)。

这个能够说是MR.Wythoff(于1907年提出此游戏)一辈子所有的贡献。不知道高斯取整函数与Beatty定理,所作的只能是找规律而已。能够先在http://www.cut-the-knot.org/pythagoras/withoff.shtml玩几局。

简单分析一下,容易知道两堆石头地位是同样的,咱们用余下的石子数(a,b)来表示状态,并画在平面直角坐标系上。

用定理: 有限个结点的无回路有向图有惟一的核  中所述的方法寻找必败态。先标出(0,0),而后划去全部(0,k),(k,0),(k,k)的格点;而后找y=x上方未被划去的格点,标出(1,2),而后划去(1,k),(k,2),(1+k,2+k),同时标出对称点(2,1),划去(2,k),(1,k),(2+k,1+k);而后在未被划去的点中在y=x上方再找出(3,5)。。。按照这样的方法作下去,若是只列出a<=b的必败态的话,前面的一些是(0,0),(1,2),(3,5),(4,7),(6,10),…

接下来就是找规律的过程了,忽略(0,0),记第n组必败态为(a[n],b[n])

命题一:a[n+1]=前n组必败态中未出现过的最小正整数

[分析]:若是a[n+1]不是未出现的数中最小的,那么能够从a[n+1]的状态走到一个使a[n+1]更小的状态,和咱们的寻找方法矛盾。

命题二:b[n]=a[n]+n

[分析]:概括法:若前k个必败态分别为 ,下证:第k+1个必败态为

从该第k+1个必败态出发,一共可能走向三类状态,从左边堆拿走一些,从右边堆拿走一些,或者从两堆中拿走一些.下面证实这三类都是胜态.

状况一:由命题一,任意一个比a[k+1]小的数都在以前的必败态中出现过,一旦把左边堆拿少了,咱们只要再拿成那个数相应的必败态便可。

状况二(从右边堆拿走不太多):这使得两堆之间的差变小了,好比拿成了 ,则可再拿成 ;

状况二(从右边堆拿走不少):使得右边一堆比左边一堆更少,这时相似于状况一,好比拿成了 (其中a[m] ;

状况三:好比拿成 ,则可再拿成 .

综上所述,任何从 出发走向的状态均可以走回核中.故原命题成立.

以上两个命题对于肯定(a[n],b[n])是完备的了,给定(0,0)而后按照这两个命题,就能够写出(1,2),(3,5),(4,7),…

这样咱们获得了这个数列的递推式,如下咱们把这两个命题当成是(a[n],b[n])的定义。

先证实两个性质:

性质一:核中的a[n],b[n]遍历全部正整数。

[分析]:由命题一,二可得a[n],b[n]是递增的,且由a[n]的定义显然。

性质二:A={a[n]:n=1,2,3,…},B={b[n]:n=1,2,3,…},则集合A,B不交。

[分析]:由核是内固集,显然。


实际上a[n]和b[n]就是一个Beatty序列。

(Betty 定理):若是存在正无理数 A, B 知足 1/A + 1/B = 1,那么集合 P = { [A*t], t ∈ Z+}、Q = { [B*t], t ∈ Z+} 恰为集合 Z+ 的一个划分,即:P ∪ Q = Z+,P ∩ Q = ø。证实见附录2。
考虑到Betty定理中“恰为Z+的划分”这一说,这意味着,Z+中的每一个数都刚好出现一次,与上述矩阵的性质十分吻合。

因而咱们猜测每一行第一列的数知足 [Φi] 的形式,获得每一行第二列的数为 [Φi] + i = [Φi + i] = [(Φ + 1)i]
咱们的目的是要让 Z+ 中每一个数都在这个矩阵中出现,因而考虑到 Betty 定理的条件,Φ 和 (Φ + 1) 应知足 1/Φ + 1/(Φ + 1) = 1。解这个方程,咱们获得 Φ = (sqrt(5) + 1) / 2,因而 Φ + 1 = (sqrt(5) + 3) / 2。
 ,到此,咱们找到了该必败态的通项公式。

实际上这组Beatty序列还有一些别的性质,好比当一个数是Fibonacci数的时候,另外一个数也是Fibonacci数;并且二者的比值也愈来愈接近黄金比,这些性质在获得通项公式以后不难证实。

启示:首先用定理所说的方法找核,而后给出核的规律(递推,或是通项)而且证实。


最后附上一张对应的必败态图.

wythoff


2.Betti theorem:
设a、b是正无理数且1/a+1/b=1。记P={[na]|n为任意的正整数},Q={[nb]|n为任意的正整数},([x]'指的是取x的整数部分)则P与Q是Z+的一个划分,即P∩Q为空集且P∪Q为正整数集合Z+。
证实:
由于a、b为正且1/a+1/b=1,则a、b>1,因此对于不一样的整数n,[na]各不相同,相似对b有相同的结果。所以任一个整数至多在集合P或Q中出现一次。
* 现证实P∩Q为空集(反证法):假设k为P∩Q的一个整数,则存在正整数m、n使得[ma]=[nb]=k。即k < ma、nb<k+1,等价地改写不等式为
 m/(k+1)< 1/a < m/k及n/(k+1)< 1/b < n/k。相加起来得 (m+n)/(k+1) < 1 < (m+n)/k,即 k < m+n < k+1。这与m、n为整数有矛盾,因此P∩Q为空集。
 *现证实Z+=P∪Q;已知P∪Q是Z+的子集,剩下来只要证实Z+是P∪Q的子集。(反证法)假设Z+\(P∪Q)有一个元素k,则存在正整数m、n使得[ma]< k <[(m+1)a]、[nb]< k <[(n+1)b]。 由此得ma < k ≦[ (m+1)a]-1<(m+1)a -1(由于a是无理数),相似地有nb < k ≦[ (n+1)b]-1<(n+1)b -1。等价地改写为 m/k < 1/a < (m+1)/(k+1)及n/k < 1/b < (n+1)/(k+1)。两式加起来,得 (m+n)/k < 1 < (m+n+2)/(k+1),即m+n < k < k+1 < m+n+2。这与m, n, k皆为正整数矛盾。 因此Z+=P∪Q。