NIM(2)“拈”游戏分析

1. 问题

  有N块石头和两个玩家A和B,玩家A先将石头分成若干堆,然后按照BABA……的顺序不断轮流取石头,能将剩下的石头一次取光的玩家获胜。每次取石头时,每个玩家只能从若干堆石头中任选一堆,取这一堆石头中任意数目(大于1)个石头。

请问:玩家A有必胜策略吗?要怎么分配和取石头才能保证自己有把握取胜?

2. 解法与分析

据说,该游戏起源于中国,英文名字叫做“NIM”,是由广东话“拈”(取物之意)音译而来,经由当年到美洲打工的华人流传出去,这个游戏一个常见的变种是将十二枚硬币分三列排成 [3,4,5] 再开始玩 。我们这里讨论的是一般意义上的“拈”游戏。

言归正传,在面试者咄咄逼人的目光下,你要如何着手解决这个问题?

在面试中,面试者考察的重点不是“what”——能否记住某道题目的解法,某件历史事件发生的确切年代,C++语言中关于类的继承的某个规则的分支等。面试者很想知道的是“how”——应聘者是如何思考和学习的。

所以,应聘者得展现自己的思路。解答这类问题应从最基本的特例开始分析。我们用N表示石头的堆数,M表示总的石头数目。

当N=1时,即只有一堆石头——显然无论你放多少石头,你的对手都能一次全拿光,你不能这样摆。

当N=2时,即有两堆石头,最简单的情况是每堆石头中各有一个石子(1,1)——先让对手拿,无论怎样你都可以获胜。我们把这种在双方理性走法下,你一定能够赢的局面叫作安全局面。

当N = 2,M > 2时,既然(1, 1)是安全局面,那么(1, X)都不是安全局面,因为对手只要经过一次转换,就能把(1, X)变成(1, 1),然后该你走,你就输了。既然(1, X)不安全,那么(2, 2)如何?经过分析,(2,2)是安全的,因为它不能一步变成(1,1)这样的安全局面。这样我们似乎可以推理(3, 3)、(4, 4),一直到(X, X)都是安全局面。

于是我们初步总结,如果石头的数目是偶数,就把它们分为两堆,每堆有同样多的数目。这样无论对手如何取,你只要保证你取之后是安全局面(X, X),你就能赢。

好,如果石头数目是奇数个呢?

当M=3的时候,有两种情况,(2, 1)、(1, 1, 1),这两种情况都会是先拿者赢。

当M=5的时候,和M=3类似。无论你怎么摆,都会是先拿者赢。

若M=7呢?情况多起来了,头有些晕了,好像也是先拿者赢。

我们在这里得到一个很重要的阶段性结论:

当摆放方法为(1, 1,…, 1)的时候,如果1的个数是奇数个,则先拿者赢;如果1的个数是偶数个,则先拿者必输。

当摆放方法为(1, 1,…, 1, X)(多个1,加上一个大于1的X)的时候,先拿者必赢。因为:

如果1有奇数个,先拿者可以从(X)这一堆中一次拿走X-1个,剩下偶数个1——接下来动手的人必输。

如果有偶数个1,加上一个X,先拿者可以一次把X都拿光,剩下偶数个1——接下来动手的人也必输。

当然,游戏是两 个人玩的,还有其他的各种摆法,例如当M = 9的时候,我们可以摆为(2, 3, 4)、(1, 4, 4)、(1, 2, 6),等等,这么多堆石头,它们既互相独立,又互相牵制,那如何分析得出致胜策略呢?关键是找到在这一系列变化过程中有没有一个特性始终决定着输赢。这个 时候,就得考验一下真功夫了,我们要想想大学一年级数理逻辑课上学的异或(XOR)运算。异或运算规则如下:

XOR(0, 0)= 0

XOR(1, 0)= 1

XOR(1, 1)= 0

首先我们看整个游戏过程,我们从N堆石头(M1, M2, …, Mn)开始,双方斗智斗勇,石头一直递减到全部为零(0, 0,…, 0)。

当M为偶数的时候,我们的取胜策略是把M分成相同的两份,这样就能取胜。

开始:(M1, M1)      它们异或的结果是XOR(M1, M1)= 0

中途:(M1, M2)      对手无论怎样从这堆石头中取,XOR(M1, M2)!= 0

我方:(M2, M2)      我方还是把两堆变相等。XOR(M2, M2)= 0

最后:(M2, M2)      我方取胜

类似的,若M为奇数,我们把石头分成(1, 1, …,1)奇数堆的时候,XOR(1, 1,…,1)[奇数个] !=0。而这时候,对方可以取走一整堆,XOR(1, 1,…, 1)[偶数个]=0,如此下去,我方必输。

我们推广到M为奇数,但是每堆石头的数目不限于1的情况,看看XOR值的规律:

开始:(M1, M2, …, Mn)           XOR(M1, M2, … Mn)=?

中途:(M1’, M2’, … Mn’)        XOR(M1’, M2’, … Mn’)=?

最后:(0, 0, …, 0)                   XOR(0,0,… 0)=0

不幸的是,可以看出,当有奇数个石头时,无论你如何分堆,XOR(M1, M2, … Mn) 总是不等于0!因为必然会有奇数堆有奇数个石头(二进制表示最低位为1),异或的结果最低位肯定为1。                                                                                            [结论1]

再不幸的是,还可以证明,当XOR(M1, M2, … Mn)!= 0时,我们总是只需要改变一个Mi的值,就可以让XOR(M1, M2, …Mi’,… Mn)= 0。                        [结论2]

更不幸的是,又可以证明,当XOR(M1, M2, … Mn)= 0时,对任何一个M值的改变(取走石头),都会让XOR(M1, M2, …Mi’,… Mn)! = 0。                      [结论3]

有了这三个“不幸”的结论,我们不得不承认,当M为奇数时,无论怎样分堆,总是先动手的人赢。

还不信?那我们试试看:当M=9,随机分堆为(1,2,6)

开始:(1,2,6)

1=0 0 1
       2=0 1 0
       6=1 1 0
    XOR=1 0 1            即XOR(1,2,6)!=0

B先手:(1, 2, 3),即从第三堆取走三个,得到(1,2,3)

1=0 0 1
       2=0 1 0
       3=0 1 1
    XOR=0 0 0            所以,XOR(1,2,3)=0

A方:(1, 2, 2)XOR(1, 2, 2)!=0。

B方:(0, 2, 2)XOR (0, 2, 2)=0

……A方继续顽抗……

B方最后:(0, 0, 0),XOR(0, 0, 0)= 0

好了,通过以上的分析,我们不但知道了这类问题的答案,还知道了游戏的规律,以及如何才能赢。XOR,这个我们很早就学过的运算,在这里帮了大忙。我们应该对XOR说Orz才对!

有兴趣的读者可以写一个程序,返回当输入为(M1, M2, …, Mn)的时候,到底如何取石头,才能有赢的可能。比如,当输入为(3, 4, 5)的时候,程序返回(1, 4, 5)——这样就转败为胜了!程序如下:

 package chapter1youxizhileNIM2;
/**
* NIM(2)"拈"游戏分析
* @author DELL
*
*/
public class NIM2 {
private int[] a; //分堆数组
//构造方法
public NIM2(int[] a){
this.a = a;
}
//获取取石头的方法
public void getResult(){
int temp; //临时存储计算结果
int sum = a[0]; //存储所有数组元素的异或结果
int i,j;
System.out.print("原始分堆情况:(");
for(i=0;i<a.length-1;i++){
System.out.print(a[i]+",");
}
System.out.println(a[a.length-1]+")");
for(j=1;j<a.length;j++){
sum ^= a[j];
}
for(i=0;i<a.length;i++){
temp = sum^a[i];
if(temp<a[i]&&temp>=0){
a[i] = temp;
break;
}
}
if(i>=a.length)
System.out.println("怎样取都无法取胜!");
else{
System.out.print("取完后的分堆情况:(");
for(i=0;i<a.length-1;i++){
System.out.print(a[i]+",");
}
System.out.println(a[a.length-1]+")");
}
}
public static void main(String[] args) {
int[] a = {3,4,5};
NIM2 nima = new NIM2(a);
nima.getResult();
int[] b = {1,2,6};
NIM2 nimb = new NIM2(b);
nimb.getResult();
} }

该程序的时间复杂度为O(N),N为堆的个数。

程序运行结果如下:

原始分堆情况:(3,4,5)
取完后的分堆情况:(1,4,5)
原始分堆情况:(1,2,6)
取完后的分堆情况:(1,2,3)

3. 扩展问题

1.如果规定相反,取光所有石头的人输,又该如何控制局面?

分析方法同上。

2.如果每次可以挑选任意K堆,并从中任意取石头,又该如何找到必胜策略呢?

分析方法同第1章 游戏之乐——NIM(1)一排石子的游戏扩展问题。

第1章 游戏之乐——NIM(2)“拈”游戏分析的更多相关文章

  1. 第1章 游戏之乐——NIM(3)两堆石头的游戏

    NIM(3)两堆石头的游戏 1. 问题描述 假设有两堆石头,有两个玩家会根据如下的规则轮流取石头:每人每次可以从两堆石头中各取出数量相等的石头,或者仅从一堆石头中取出任意数量的石头:最后把剩下的石头一 ...

  2. 第1章 游戏之乐——NIM(1)一排石子的游戏

    NIM(1)一排石子的游戏 转载:编程之美-MIN(1)一排石头的游戏 1. 原题 1.1 题目 N块石头排成一行,每块石头有各自固定的位置.两个玩家依次取石头,每个玩家每次可以取其中任意一块石头,或 ...

  3. 怎样成为一个游戏制作人——第五章:使用GGE图形库来写游戏

    怎样成为一个游戏制作人--第五章:使用GGE图形库来写游戏 前言: 细致想了一下,来看博客的一般都是有自学能力的了.C++基础多少也会有一些了. 于是决定以下的章节.会教大家做一些小游戏. 来巩固自己 ...

  4. Nim 游戏、SG 函数、游戏的和

    Nim游戏 Nim游戏定义 Nim游戏是组合游戏(Combinatorial Games)的一种,准确来说,属于“Impartial Combinatorial Games”(以下简称ICG).满足以 ...

  5. (linux shell)第二章--命令之乐(一)

    文章来自于我的个人博客:(linux shell)第二章--命令之乐(一)    上一章我们描写叙述了一些linux shell中须要注意的一些语法.接下来我们開始了解linux shell的经常使用 ...

  6. 51nod1069【Nim取石子游戏】

    具体看:萌新笔记之Nim取石子游戏可以这么写: #include <bits/stdc++.h> using namespace std; typedef long long LL; in ...

  7. 萌新笔记之Nim取石子游戏

    以下笔记摘自计算机丛书组合数学,机械工业出版社. Nim取石子游戏 Nim(来自德语Nimm!,意为拿取)取石子游戏. 前言: 哇咔咔,让我们来追寻娱乐数学的组合数学起源! 游戏内容: 有两个玩家面对 ...

  8. Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏

    Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏 即使是如今,非常多初学游戏开发的同学.在谈到Unity的时候.依旧会觉得Unity仅仅能用于制作3D游戏的. 实际上.Unity在2013 ...

  9. NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结

    NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结 经典NIM游戏: 一共有N堆石子,编号1..n,第i堆中有个a[i]个石子. 每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子 ...

随机推荐

  1. KVO KVC

    @interface FoodData : NSObject { NSString * foodName; float foodPrice; } @end ////////////////////// ...

  2. STM32 TIM重映射

    复用功能 没有重映射 部分重映射 完全重映射 TIM3_CH1 PA6 PB4 PC6 CH2 PA7 PB5 PC7 CH3 PB0 PB0 PC8 CH4 PB1 PB1 PC9 /**重映射 t ...

  3. 连分数(分数类模板) uva6875

    //连分数(分数类模板) uva6875 // 题意:告诉你连分数的定义.求连分数,并逆向表示出来 // 思路:直接上分数类模板.要注意ai可以小于0 #include <iostream> ...

  4. ubuntu java jdk安装及环境变量设置

    1.下载jdk1.7.0_79 (32位操作系统)jdk-7u79-linux-i586.tar.gz (64位操作系统)jdk-7u79-linux-x64.tar.gz http://www.or ...

  5. leetcode@ [310] Minimum Height Trees

    For a undirected graph with tree characteristics, we can choose any node as the root. The result gra ...

  6. HDU 5831 Rikka with Parenthesis II (栈+模拟)

    Rikka with Parenthesis II 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5831 Description As we kno ...

  7. POJ 3026 Borg Maze (最小生成树)

    Borg Maze 题目链接: http://acm.hust.edu.cn/vjudge/contest/124434#problem/I Description The Borg is an im ...

  8. Configure the handler mapping priority in Spring MVC

    Often times, you may mix use of multiple handler mappings strategy in Spring MVC development. For ex ...

  9. STC89C52RC片内资源介绍

    STC89C52RC片内有:用户应用程序区(AP)8K,地址0000h-1FFFh. 数据flash区(EEPROM)4K,2000h-2FFFh ISP引导区空间1K/2k/4k. RAM 512B ...

  10. zookeeper的配置项

    1 tickTime:CS通信心跳数 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳.tickTime以毫秒为单位. tick ...