取石子游戏详解NIM

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

目录(?)[+]

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

取石子游戏详解NIM

取石子游戏详解NIM

取石子游戏详解NIM

取石子游戏详解NIM

取石子游戏详解NIM

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

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

经典的三种玩法:

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

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

三、威佐夫博奕(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,奇异局势有如下三条性质:
    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 - 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 plaincopy
  1. /****************************************************************************/
  2. /*      编程之美 - NIM捡石子问题 皮皮 2014-9-10        */
  3. /****************************************************************************/
  4. #include <assert.h>
  5. #include <stdio.h>
  6. #include <math.h>
  7. /*  黄金分割数最优算法   */
  8. static int nim(int x, int y){
  9. double a = (sqrt(5.0) + 1) / 2;             //goldenNum 1.618...
  10. int n = abs(x - y);                         //bn - an
  11. x = x < y? x : y;                            //取小者
  12. return ( x != int(a * n) );
  13. }
  14. int main(){
  15. int x, y;
  16. while(scanf("%d%d", &x, &y) == 2)
  17. printf("%d\n", nim(x, y));
  18. return 0;
  19. }

拓展:?

【综合一、三】

任给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数;而且两者的比值也越来越接近黄金比,这些性质在得到通项公式之后不难证明。

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

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

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。

取石子游戏详解NIM相关推荐

  1. 博弈论之双人取数游戏详解

    描述 有如下一个双人游戏:N(2 <= N <= 100)个正整数的序列放在一个游戏平台上,游戏由玩家1开始,两人轮流从序列的两端取数,取数后该数字被去掉并累加到本玩家的得分中,当数取尽时 ...

  2. 博弈论之取石子游戏的学习

    以下内容来自转载: 博弈问题简介 所讨论的博弈问题满足以下条件: 玩家只有两个人,轮流做出决策 游戏的状态集有限,保证游戏在有限步后结束,这样必然会产生不能操作者,其输 对任何一种局面,胜负只决定于局 ...

  3. poj 1067 取石子游戏(博弈+威佐夫博奕(Wythoff Game))

    取石子游戏 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 29959   Accepted: 9818 Descriptio ...

  4. POJ1067 取石子游戏 跪跪跪,很好的博弈论

    取石子游戏 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 29951   Accepted: 9814 Descriptio ...

  5. HDU2516 取石子游戏【博弈】

    取石子游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  6. bzoj 1413: [ZJOI2009]取石子游戏(博弈+DP)

    1413: [ZJOI2009]取石子游戏 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 711  Solved: 470 [Submit][Sta ...

  7. BZOJ 1413: [ZJOI2009]取石子游戏 博弈+Dp

    title BZOJ 1413 Description 在研究过Nim游戏及各种变种之后,Orez又发现了一种全新的取石子游戏,这个游戏是这样的: 有n堆石子,将这n堆石子摆成一排.游戏由两个人进行, ...

  8. Python Tkinter——数字拼图游戏详解版

    Python Tkinter 实践系列--数字拼图游戏详解版 import random #Python中的random是一个标准库用于生成随机数.随机整数.还有随机从数据集取数据. import t ...

  9. OPJ-1067 取石子游戏 解题报告(数论) 取石子游戏,betty定理

    连接--A - 取石子游戏 Time Limit:1000MS    Memory Limit:10000KB    64bit IO Format:%I64d & %I64u Descrip ...

最新文章

  1. 编写测试用例的方法_适合小白的测试用例编写方法,一看就会!
  2. nodejs中的全局函数setTimeout/clearTimeout,setInterval/clearInterval,unref/ref
  3. 【OJ】OJ的介绍和常用OJ推荐
  4. 议rman的crosscheck和obsolete
  5. node版本查看管理工具
  6. 微软认真聆听了开源 .NET 开发社区的炮轰: 通过CLI 支持 Hot Reload 功能
  7. IDEA上Debug调试全流程
  8. .NetCore实践篇:成功解决分布式监控ZipKin聚合依赖问题(三)
  9. java 20段常用代码,你绝对用的到(二)
  10. 【博士后招聘】浙江大学杨杰课题组-医学AI/大数据分析/自然语言处理
  11. 更改计算机睡眠时间注册表,如何通过修改注册表开启/关闭系统休眠?
  12. uniapp手机号码正则验证
  13. PLSQL使用SYSTEM用户连接报错:ORA-01031: insufficient privileges
  14. “怎样解题”-第二部分:怎样题解——作者:波利亚
  15. Petalinux EMMC启动
  16. Ubuntu18.04-使用wine打开CAJ文件并解决乱码问题
  17. 小白也能看懂的手机配置攻略
  18. 创建一个网站需要多少预算?
  19. hiredis-vip
  20. 总结iPhone、iPad各机型对应的iOS 系统版本(最全)

热门文章

  1. LabVIEW通讯-GPIB
  2. 【大数据基础】vmware+ununtu安装详细过程
  3. 【AR技术】AR教学机器人
  4. 刚从阿里、头条面试回来,疯狂java讲义第五版百度网盘
  5. 手势解锁java后端设计_canvas手势解锁源码
  6. linux 部署war包到tomcat解压但未加载
  7. python数据分析(五)——numpy+matplotlib实例
  8. 类模板详解\n类模板的定义及实例化 模板参数
  9. mpc模型预测控制原理详解
  10. 大学生科创国赛最终稿