Nim与取火柴问题

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的证明来的。

实例

例1:2. 取火柴游戏的规则如下:一堆火柴有N根,A、B两人轮流取出。每人每次可以取1 根或 2 根,最先没有火柴可取的人为败方,另一方为胜方。如果先取者有必胜策略则记为1, 先取者没有必胜策略记为0。
当N 分别为100,200,300,400,500 时,先取者有无必 胜策略的标记顺序为(回答应为一个由0 和/或1 组成
的字符串)。
第一题比较简单,可以推广到有n根火柴每人可取1到m根
若n % ( m + 1 ) == 1则先取必败,否则必胜,这个比较好证明
很简单可以证明当火柴数为1时先取必败,然后火柴数为m + 1时,无论先取的人怎样取,假定取x个,那么第二个人取m - x个,即可只剩一个留给对方。这样一直下来,如果用D( x ) = 1表示当留有x个火柴时先取必败,那么
D( 1 ) = 1
D( m + 2 ) = 1
D( 2m + 3 ) = 1
........
在这道题的情况就是
D( 1 ) = 1
D( 4 ) = 1
D( 7 ) = 1
.........
所以刚开始这些情况是必败的,换言之,如果刚开始不是这种情况,则先取的人可以取n % ( m + 1 ) - 1个(注意n % ( m + 1 ) == 0的情况,这是应该是取m个),就可以转化成上述令对手必败的情况。
例2:2.(取石子游戏)现有5堆石子,石子数依次为3,5,7,19,50,甲乙两人轮流从任一堆中任取(每次只能取自一堆,不能不取),取最后一颗石子的一方获胜.甲先取,问甲有没有获胜策略(即无论乙怎样取,甲只要不失误,都能获胜) 如果有,甲第一步应该在哪一堆里取多少 请写出你的结果
只有一堆时,无论有多少,先取者都可以一次性全部取走,所以必胜。

(1,1)时,显然先取者必败。
(1,2)时,先取者必胜,他可以在2那一堆中取1个,于是变成(1,1),但这成为上一种情况了,于是接下来取的人必败,亦即先取者必胜。
(1,3)时,先取者必胜。他可以在3那一堆中取2个,于是变成(1,1)。
(2,2)时,先取者必败。他在任何一堆中取1个,对方随即在另一堆中取1个,即变成(1,1);如果他取走一堆中的全部石子,对方即取走另一堆中的全部石子。
(2,3)时,先取者必胜。他可以在3那一堆中取1个,于是变成(2,2)。
(3,3)时,先取者必败。他取走任一堆中的1,2或3个,就变成了以上讨论过的情形。

(1,1,1)时,先取者必胜。他取走任一堆,就变成了(1,1)。
(1,1,2)时,先取者必胜。他取走2那一堆,就变成了(1,1)。
(1,1,3)时,先取者必胜。他取走3那一堆,就变成了(1,1)。
(1,2,2)时,先取者必胜。他取走1那一堆,就变成了(2,2)。
(1,2,3)时,先取者必败。分析如下:
   他先取1那一堆,则变为(2,3),由上面的分析,对手必胜。
   他从2那一堆中取1个,就变成了(1,1,3),对手可以将3那一堆全部取走,变成了(1,1),于是必胜。
   他将2那一堆全部取走,就变成了(1,3),对手必胜。
   他从3那一堆中取1个,就变成了(1,2,2),对手必胜。
   他从3那一堆中取2个,就变成了(1,2,1),对手必胜。
   他将3那一堆全部取走,就变成了(1,2),对手必胜。

  这些胜负有什么规律呢?我们可以将每堆的数转换成二进制,然后看每一位上所有堆里的1的个数总和:
  必胜情况:(n) (1,2)(1,3)(2,3) (1,1,1)(1,1,2)(1,2,2)
  必败情况: (1,1)(2,2)(3,3) (1,2,3)

化为二进制:
必胜情况:
(n)<只有1堆>:……(反正每位只要有1肯定只有1个)
(1,2):1,10
  列成竖式:
  01
  10
  个位上只有1个1,“十位”(因为是二进制所以叫十位不妥,这里为了方便说明暂且使用,下同)上也只有1个1。
(1,3):1,11
  列成竖式:
  01
  11
  个位上有2个1(1的1个,3的1个),十位上有1个1。
(2,3):10,11
  个位上有1个1,十位上有2个1。
(1,1,1):1,1,1
  个位上有3个1。
(1,1,2):1,1,10
  个位上有2个1,十位上有1个1。
(1,1,3):1,1,11
  个位上有3个1,十位上有1个1。
(1,2,2):1,10,10
  个位上有1个1,十位上有2个1。

必败情况:
(1,1):1,1
  个位上有2个1。
(2,2):10,10
  十位上有2个1。
(3,3):11,11
  个位上有2个1,十位上也有2个1。
(1,2,3):1,10,11
  个位上有2个1,十位上也有2个1。

  下面分析一下这些情况。
  先看必败情形。容易发现,所有的必败情形,都是所有的数位上都有偶数个1。
  下看必胜情形。我们发现,出现了两种情况:
  1.只有1位上有奇数个1,如(1,3)(2,3)(1,1,1)(1,1,2)(1,2,2)。而先取者取走该位上的1,所有的位上就都变成了偶数个1,而这时后取者变成了先取者。
  2.有若干位上都是奇数个1,如(n)(1,2)(1,1,3)。先取者取(不一定取走哪位)后,所有的位上也都变成了偶数个1。后取者变成了先取者。
  以上两种情况,都是将后取者逼至必败情况从而取胜。

  由以上分析我们可以得到结论:将所有的堆的石子数化为二进制后,如果所有数位上的1的个数都是偶数,那么先取者必败;如果有些位上的1的个数是奇数,先取者能够将所有数位上的1的个数都变为偶数的话,那么先取者必胜。

好,下面来分析我们的题目。
3,5,7,19,50化为二进制是:
000011
000101
000111
010011
110010
可见,只有最高位的1是奇数个,其他位上都是偶数个。
所以只需要将最高位的1取走即可必胜。
二进制的100000就是10进制的32,所以要将50个石子的那堆取32个,取掉就变成偶数个数目。于是先取者必胜。以后无论对方怎么取,始终保证每一位上的1的个数是偶数即可(一种简单的方法是,他在一堆中取几个,你在另一堆中也取几个就可以)。
例3:有n根火柴,甲乙两人轮流从中拿取,一次至少拿一根,至多拿先前对方一次所取火柴数目的两倍。甲先拿,开始时甲可以拿任意数目的火柴(不能拿完)。最先没有火柴拿的一方为败方。给定一个n,问甲能否赢!(1<n<=10^9)
是斐波那契数列,如果是这个数列中的数,先手输,否则先手赢。
赢的方法就是拿掉若干根,使之成为斐波那契数列中对应的项(最接近的一项)。
定义函数f(n, m)表示当前有n根火柴,且对手上一步取了m根的情况下,有没有必胜法,若有f=1,否则f=0
若是第一次走,令m=0,其他情况m都不为0。 n是从2开始的,为了叙述方便,补充n=0和1时的定义: f(0, m)=0, f(1, m)=1 m>=1。无需定义f(1,0)和f(0,0)
对一个固定的n,必须是f(n,1), f(n,2), ..., f(n,ceiling(n/2)-1)都为0, 则f(n,0)才为0。否则f(n,0)=1。
n=2时 f(2,m)=1 m>=1 得出f(2,0)=0. 表示若共有2根火柴,先走的人必败,若是当前有2根火柴,且对手上一次取了1根或以上,当前先走的人必胜
依次类推:
f(3,1)=0, f(3,m)=1 m>=2 则f(3,0)=0  
f(4,m)=1 m>=0 则f(4,0)=1
……………………
算法是:f(n,m)=f(n-1,1) | f(n-2, 2) | ... | f(n-2m,2m) 然后取反

发现一旦f(n,m)=1, 那么右边的当n不变m增大时f的值都为1
推断f(n,0)=0的序列是Fibonacci数列
用归纳法来证明,假设n是一个Fibonacci数
有f(n, 1)=…=f(n, ceiling(n/2)-1)=0, f(n, ceiling(n/2))=1
我们发现f(n+1,m)=f(1,m), f(n+2,m)=f(2,m)......对任何m成立,也就是说f的值从头重复了一遍
但是f(n+1,0), f(n+2,0), ...都是1,因为对一个固定的n,必须是f(n,1), f(n,2), ..., f(n,ceiling(n/2)-1)都为0, 则f(n,0)才为0
第一次例外是f(n+k),其中k也是一个Fibonacci数,满足k>n/2
而满足这个条件的第一个k就是n之前的那个Fibonacci数
所以n+k是n之后的第一个Fibonacci数

你可能感兴趣的:(Nim与取火柴问题)