编程之美----NIM游戏

时间:2022-05-06
本文章向大家介绍编程之美----NIM游戏,主要内容包括: 博弈游戏·Nim游戏、输入、输出、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

: 博弈游戏·Nim游戏

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

今天我们要认识一对新朋友,Alice与Bob。 Alice与Bob总是在进行各种各样的比试,今天他们在玩一个取石子的游戏。 在这个游戏中,Alice和Bob放置了N堆不同的石子,编号1..N,第i堆中有A[i]个石子。 每一次行动,Alice和Bob可以选择从一堆石子中取出任意数量的石子。至少取1颗,至多取出这一堆剩下的所有石子。 Alice和Bob轮流行动,取走最后一个石子的人获得胜利。 假设每一轮游戏都是Alice先行动,请你判断在给定的情况下,如果双方都足够聪明,谁会获得胜利?

输入

第1行:1个整数N。表示石子堆数。1≤N≤100 第2行:N个整数,第i个整数表示第i堆石子的个数A[i],1≤A[i]≤10000

输出

第1行:1个字符串,若Alice能够获胜输出"Alice",否则输出"Bob"

样例输入

3
3 2 1

样例输出

Bob

解题报告:
    题目很简单,但是分析很有趣...下面来看下分析吧!
      对于  A[i---N] 比如:
      3   011  
    2   010
    1   001   --》处于一种平衡态  所以Alice不管如何取值都会破坏平衡态,故bob  win

   在看一个列子:   3 4 5 6
     3   011
   4   100
     5   101
     6   110    --》处于一种非平衡,因而Alice只需要挖掉一个值,使其保持平痕即可!
 
----总归与一句话,守平衡者,win
  
代码:
 1     #include<stdio.h>
 2     int main(){
 3         int N,res,tmp;
 4         while(scanf("%d",&N)!=EOF){
 5              res=0;
 6             while(N--){
 7               scanf("%d",&tmp);
 8               res^=tmp;
 9             }
10             puts(res==0?"Bob":"Alice");
11         }
12         return 0;
13     }

下面是具体的分析:

Nim游戏的概述:

还记得这个游戏吗? 给出n列珍珠,两人轮流取珍珠,每次在某一列中取至少1颗珍珠,但不能在两列中取。最后拿光珍珠的人输。 后来,在一份资料上看到,这种游戏称为“拈(Nim)”。据说,它源自中国,经由被贩卖到美洲的奴工们外传。辛苦的工人们,在工作闲暇之余,用石头玩游戏以排遣寂寞。后来流传到高级人士,则用便士(Pennies),在酒吧柜台上玩。 最有名的玩法,是把十二枚便士放成3、4、5三列,拿光铜板的人赢。后来,大家发现,先取的人只要在3那列里取走2枚,变成了1、4、5,就能稳操胜券了,游戏也就变得无趣了。于是大家就增加列数,增加铜板的数量,这样就让人们有了毫无规律的感觉,不易于把握。 直到本世纪初,哈佛大学数学系副教授查理士•理昂纳德•包顿(Chales Leonard Bouton)提出一篇极详尽的分析和证明,利用数的二进制表示法,解答了这个游戏的一般法则。 一般规则是规定拿光铜板的人赢。 它的变体是规定拿光铜板的人输,只要注意某种特殊形态(只有1列不为1),就可以了! 有很多人把这个方法写成计算机程序,来和人对抗,不知就理的人被骗得团团转,无不惊叹计算机的神奇伟大。其实说穿了,只因为它计算比人快,数的转化为二进制其速度快得非人能比,如此罢了。 (以上来自K12教育论坛

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是一个P是一个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问题就这样基本上完美的解决了。

(以上来自百度百科)

Nim游戏的形象具体论述:

Nim取子游戏是由两个人面对若干堆硬币(或石子)进行的游戏。设有k>=1堆硬币,各堆分别含有N1,N2,……NK枚硬币。游戏的目的就是选择最后剩下的硬币。游戏法则如下:

1.两个游戏人交替进行游戏(游戏人I和游戏人II);

2.当轮到每个游戏人取子时,选择这些堆中的一堆,并从所选的堆中取走至少一枚硬币(游戏人可以取走他所选堆中的全部硬币);

3.当所有的堆都变成空堆时,最后取子的游戏人即为胜者。

这个游戏中的变量是堆数k和各堆的硬币数N1,N2,……Nk。对应的组合问题是,确定游戏人I获胜还是游戏人II获胜以及两个游戏人应该如何取子才能保证自己获胜(获胜策略)。

为了进一步理解Nim取子游戏,我们考查某些特殊情况。如果游戏开始时只有一堆硬币,游戏人I则通过取走所有的硬币而获胜。现在设有2堆硬币,且硬币数量分别为N1和N2。游戏人取得胜利并不在于N1和N2的值具体是多少,而是取决于它们是否相等。设N1!=N2,游戏人I从大堆中取走的硬币使得两堆硬币数量相等,于是,游戏人I以后每次取子的数量与游戏人II相等而最终获胜。但是如果N1= N2,则:游戏人II只要按着游戏人I取子的数量在另一堆中取相等数量的硬币,最终获胜者将会是游戏人II。这样,两堆的取子获胜策略就已经找到了。

现在我们如何从两堆的取子策略扩展到任意堆数中呢?

首先来回忆一下,每个正整数都有对应的一个二进制数,例如:57(10) à 111001(2) ,即:57(10)=25+24+23+20。于是,我们可以认为每一堆硬币数由2的幂数的子堆组成。这样,含有57枚硬币大堆就能看成是分别由数量为25、24、23、20的各个子堆组成。

现在考虑各大堆大小分别为N1,N2,……Nk的一般的Nim取子游戏。将每一个数Ni表示为其二进制数(数的位数相等,不等时在前面补0):

N1 = as…a1a0

N2 = bs…b1b0

……

Nk = ms…m1m0

如果每一种大小的子堆的个数都是偶数,我们就称Nim取子游戏是平衡的,而对应位相加是偶数的称为平衡位,否则称为非平衡位。因此,Nim取子游戏是平衡的,当且仅当:

as + bs + … + ms 是偶数

……

a1 + b1 + … + m1 是偶数

a0 + b0 + … + m0是偶数

于是,我们就能得出获胜策略:

游戏人I能够在非平衡取子游戏中取胜,而游戏人II能够在平衡的取子游戏中取胜。

我们以一个两堆硬币的Nim取子游戏作为试验。设游戏开始时游戏处于非平衡状态。这样,游戏人I就能通过一种取子方式使得他取子后留给游戏人II的是一个平衡状态下的游戏,接着无论游戏人II如何取子,再留给游戏人I的一定是一个非平衡状态游戏,如此反复进行,当游戏人II在最后一次平衡状态下取子后,游戏人I便能一次性取走所有的硬币而获胜。而如果游戏开始时游戏牌平衡状态,那根据上述方式取子,最终游戏人II能获胜。

下面应用此获胜策略来考虑4-堆的Nim取子游戏。其中各堆的大小分别为7,9,12,15枚硬币。用二进制表示各数分别为:0111,1001,1100和1111。于是可得到如下一表:

23 = 8

22 = 4

21 = 2

20 = 1

大小为7的堆

0

1

1

1

大小为9的堆

1

0

0

1

大小为12的堆

1

1

0

0

大小为15的堆

1

1

1

1

由Nim取子游戏的平衡条件可知,此游戏是一个非平衡状态的取子游戏,因此,游戏人I在按获胜策略进行取子游戏下将一定能够取得最终的胜利。具体做法有多种,游戏人I可以从大小为12的堆中取走11枚硬币,使得游戏达到平衡(如下表),

23 = 8

22 = 4

21 = 2

20 = 1

大小为7的堆

0

1

1

1

大小为9的堆

1

0

0

1

大小为12的堆

0

0

0

1

大小为15的堆

1

1

1

1

之后,无论游戏人II如何取子,游戏人I在取子后仍使得游戏达到平衡。

同样的道理,游戏人I也可以选择大小为9的堆并取走5枚硬币而剩下4枚,或者,游戏人I从大小为15的堆中取走13枚而留下2枚。

归根结底,Nim取子游戏的关键在于游戏开始时游戏处于何种状态(平衡或非平衡)和第一个游戏人是否能够按照取子游戏的获胜策略来进行游戏。

(以上转自Rainco_shnu的百度空间)

下面写点自己的东西:

(2)如果Nim游戏中的规则稍微变动一下,每次最多只能取K个,怎么处理?

      方法是将每堆石子数mod (k+1)

应博友的请求,现在花些时间来说说这个,对于NIM博弈游戏,关于每次取最少取一个最多取k的情况....。

不过在之前,我们先来看另一种重要的博弈《美丽心灵》的主角巴什----巴什博弈。 

     只有一堆n个物品的情况,两人,比如王二小,和王小二轮流从这堆物品中取物,规定每次至少取一个,对多取k个,最后取光者win......。

 那么对于  n<=k   的情况, 谁先取,谁win 。

               n > k  ;  那么我不妨设定  n =  a*k +b ; ( a,b  皆属于自然数 ) ,显然我们每次留下k的倍数给对方,己方的优劣得不到确定。但是正是由于k值的限定,我们便可以很定的说, 对于任意的w自然数小于n, 如果剩余数为 :  w+n (w=1,2,3,4,5,...n-1),当然我们可以很容易明白,只有当w=1时,即最后一个状态为 1+n 时 ,谁拿谁输 。   所以对于先手,策略是,只需要留下n+w(w=1,因为最少取一个)的倍数,就可以保证自己的优势。

     所以,不难得到  n = (k+1)*a +b ;   对于先手每次只需要取 b个数,就可以取胜。

那么对于这个NIM博弈,最少取一,最多取k的情况呢 ?

  对于每一堆的情况,我们可以认同其为一个巴什博弈特化,但是总体又是Nim博弈.....

所以解决的方法是: 将每一堆石子数mod(k+1),然后进行二级制的异或操作。当然考虑了这个情况,我们不妨再来看看另一种情况,

(3) 对于NIM博弈游戏,关于每次取最少取t个最多取k的情况....。

  方法也是: 

所以解决的方法是: 将每一堆石子数mod(k+1),然后进行二级制的异或操作。当然考虑了这个情况,我们不妨再来看看另一种情况。