NIM博弈 基础

Nim 博弈游戏

本文参考文献:https://www.zhihu.com/question/29910524 Simos 的回答

博弈论最经典的模型之一.
最常见的游戏为,给你好几列(或者堆)的棋子, 每次可以且必须在某一堆棋子里面拿走1-n(n为这堆棋子的最大个数)个棋子,然后再由另一个人拿(规则相同),最终谁拿走了全部的棋子谁就赢. 玩家为先手.
第一堆: XXXXXX 数目为 a1
第二堆: XXXXXX 数目为 a2
第三堆: XXXXXX 数目为 a3

第n堆: XXXXXX 数目为 an

Solution:

一. NIM 博弈, 最重要的是寻找必败态(这是一个坑爹的游戏, 一定存在某种局面,使得你先走你就输了).
通俗的、这个必败态的的意思就是,这样一种局面摆在面前的话先手必败。
严格的:

  1. 无法进行任何移动的局面,为必败态。(比如我面对的棋子为空, 代表着我已经输了)
  2. 可以移动到 必败态的局面为非必败态
  3. 在必败态做的左右操作都是非必败态
    1> 所以我们的赢法:

我们处于 非必败态,要想办法把棋子移动到 必败态, 而对方只能把 必败态 的棋子转换到 非必败态。
对方一定给我们非必败态的棋子 --> 我们想办法把棋子转换为必败态 --> 对方一定且只能把棋子由 必败态 转换为 非必败态 … 无限循环,我们赢了!!!

2> 我们的输法:

对方一定给我们非必败态的棋子 --> 我们随便拿走了几个棋子,转换为非必败态 --> 对方想办法把棋子转换为必败态 … 无限循环,我们输了~~~

如何表示必败态

a1a2a3…a^n == 0
证明:
要证明 a1a2a3…a^n == 0, 即证明 其满足上文提到的必败态的定义。

对于上面的三种状态(以二进制思考)

  1. 无法进行任何移动的局面,为必败态, 必败态必然满足 a1 == a2 == a3 == a4 == 0, 必然可以等价用异或表示为: a1a2a3^a4 == 0 ,
    证得 a1a2a3^a4 == 0, 局面必然为必败态。

  2. 可以移动到 必败态的局面为非必败态.
    抽象为 证明 若存在某个非必败态局面 A 为 a1a2a3…an != 0 (A 不为必败态) 则一定可以存在一种移动 将其改变成局面 A’ 后满足 B == 0 (A’ 为必败态)
    那么我们用 异或 来表示改变这个局面啊,设用来进行异或操作的 的二进制码为 atmp
    即证明 A’ == A^atmp == a1a2a3…an^atmp == 0
    由于对任何一个数而言,一定存在一个数 tmp使得 这个数^tmp == 0
    ∴ A^atmp == 0 必然成立, ∴ 必然存在一种移动可以使得一个非必败态局面移动到必败态局面。 即可以移动到 必败态的局面为非必败态(因为只有两种局面).

3.在必败态做的左右操作都是非必败态
存在一个二进制码B == 0(为必败态),改变任何一位,都会导致其不为 0(不为必败态)

综上所述, a1a2a3…a^n == 0 完全满足上述三个对于必败态的定义. ∴ 可以用 a1a2a3…a^n == 0 表示必败态.
证明完毕.

PS: 关于异或的性质如下:

  1. 自己是自己的逆运算 (ab)b == a
  2. 满足消去率
    a-c = b-c --> a == b
    a+c = b+c --> a == b
    a^c = b^c --> a == b

二. 次重要的是,1> 如何将 非必败态 转换为 必败态??? 2> 如何判断当前局面是 什么态??

1>

int shoud_do;
int i;
for(i = 1; i <= n; ++i)//便利a1~an
{
  shoud_do = aI[i] ^ atmp;
  if(shoud_do <= aI[i] )//如果异或后的结果 <= 当前 aI, 则此方案可行 ( 因为数目只能减少而不能增加)
  {
    break;
  }
}

cout << "将第" << i << "行" << "减少到" << shoud_do << "位" << endl;

用 A 中最大的 ax = ax || atmp
则,达到平衡态, 现在 ax 的数目为

2>

//伪代码
if(a1^a2^a3..^an == 0){
  "为必败态"
}

至此为止,最基础的 NIM 博弈就完成了.
本文参考文献:https://www.zhihu.com/question/29910524 Simos 的回答

完整代码:

#include 
#include 
using namespace std;
//对方已经摆好了棋盘,且为非平衡态,请把棋盘局面变为平衡态.

//0011
//1000
//0001
//^
//1010  atmp
//--------------------------------
//0000

//1010
//1111 ||
//----
//1111

//   2. 对方一定会尽量的阻止平衡状态的出现,

int aI[1000];
int MaxBitsLength;
int Max_ax_Index;
int n;

int SizeOfBitLength(int bits)
{
  int bits_length = 0;
  while(bits)
  {
    bits >>= 1;
    ++bits_length;
  }
  return bits_length;
}

int input( )
{
  int result_of_all_row_xor = 0;
  aI[0] = 0;
  for( int  i = 1; i <= n; ++i)
  {
    cin >> aI[i];
    result_of_all_row_xor ^= aI[i];

    if(aI[i] > aI[i - 1] )
    {
      MaxBitsLength = SizeOfBitLength( aI[i] );
      Max_ax_Index = i;
    }
  }
  return result_of_all_row_xor;
}


int main()
{

  //输入地图
  while( cin >> n )
  {
    int result_of_all_row_xor  = input();

    //cout << "result_of_all_row_xor: " << result_of_all_row_xor << endl;

    int atmp;
    //cout << MaxBitsLength << endl;

    int all_bit_is_1 = ~(1 << ( MaxBitsLength - 1 )) ^ (-1 << ( MaxBitsLength - 1 )); //希望是 15

    //cout << "all_bit_is_1: " << all_bit_is_1 << endl;
    if(  result_of_all_row_xor == 0)
    {
      cout << " 你已经输了,蠢货!!, 有我知道还能错" << endl;
      continue;
    }

    for(atmp = 1; atmp <= all_bit_is_1; ++atmp)
    {
      if( (result_of_all_row_xor ^ atmp) == 0 ) //即状态平衡了
      {
        break;
      }
    }

    int shoud_do;
    int i;
    for(i = 1; i <= n; ++i)
    {
      shoud_do = aI[i] ^ atmp;
      if(shoud_do <= aI[i] )
      {
        break;
      }
    }

    //cout << aI[Max_ax_Index] << ' ' <<  atmp << endl;

    cout << "将第" << i << "行" << "减少到" << shoud_do << "位" << endl;
    cout << " -------------------------- " << endl;
  }
}

欢迎阅读,NIM 博弈 进阶: 文章为完成,以后附上链接.

你可能感兴趣的:(CS61C,计算机组成原理)