我们强烈建议您参考以下文章作为此操作的先决条件。
组合博弈论|设置1(简介)
在这篇文章中,讨论了《尼姆游戏》。 Nim的游戏由以下规则描述:
给定许多堆,其中每堆包含一定数量的石头/硬币。在每一回合中,玩家只能选择一个堆,并从该堆中清除任意数量的石头(至少一个)。不能移动的玩家被认为输掉了比赛(即,拿到最后一块石头的人是赢家)。 ”
例如,假设有两个玩家A和B ,并且最初有三堆硬币,最初每个堆中都有3、4、5个硬币,如下所示。我们假设第一步是A。请参阅下图,以清楚地了解整个游戏过程。
A赢得比赛(注意:A采取了第一步)
那么A在此游戏中拥有强大的专业知识吗?还是他/她先开始比B有优势?
现在让我们再次进行游戏,使用与上述相同的桩组配置,但是这次B开始而不是A开始。
B赢得比赛(注意:B采取了第一步)
B Y形如上图所示,必须明确的是,比赛取决于一个重要因素-谁先开始游戏?
那么,先发球员会每次赢吗?
让我们再次从A开始玩游戏,这次是使用不同的桩初始配置。堆最初有1、4、5个硬币。
A会因为他先开始而再次获胜吗?让我们来看看。
A采取了第一步,但输掉了比赛。
因此,结果很明显。 A已经输了。但是如何?我们知道这个游戏很大程度上取决于哪个玩家首先开始。因此,必须有另一个因素来主导这个简单有趣的游戏的结果。该因素是堆/堆的初始配置。这次的初始配置与先前的配置不同。
因此,我们可以得出结论,该游戏取决于两个因素:
- 首先开始的玩家。
- 堆/堆的初始配置。
实际上,我们甚至可以在玩游戏之前就预测游戏的赢家!
Nim-Sum:在游戏的任何一点上,每堆/堆中硬币/石头的数量的累积XOR值在该点称为Nim-Sum。
“如果A和B的比赛都达到最佳状态(即,他们没有犯任何错误),那么如果游戏开始时的Nim-Sum不为零,则保证先发动的玩家获胜。否则,如果Nim-Sum计算为零,则玩家A肯定会输。”
有关上述定理的证明,请参见-https://en.wikipedia.org/wiki/Nim#Proof_of_the_winning_formula
最佳策略:
- 理解最佳策略所必需的关于按位XOR的几个推论:
- 如果“ n”个数的XOR总和已经为零,则不可能通过对数进行一次约简来使XOR总和为零。
- 如果“ n”个数字的XOR总和不为零,那么至少有一种方法可以减少一个数字,即XOR总和为零。
最初可能存在两种情况。
情况1:初始Nim Sum为零
众所周知,在这种情况下,如果B获胜,则B总是希望A的回合的Nim总和为零。
因此,由于Nim Sum最初为零,因此无论A删除新Nim Sum的项目数量为多少(如上所述)。另外,由于B在A的转弯中希望Nim的总和为零,因此他将随后移动,以使Nim Sum的总和再次为零(如上所述,这始终是可能的)。
只要堆中有任何物品,游戏就会继续进行,并且在它们各自的回合中, A将使Nim总和为非零, B将使其再次为零,最终将不剩任何元素,而B为其中一个选择最后的胜利游戏。
通过以上解释可以明显看出,每个玩家的最佳策略是使对手的Nim Sum在每个回合中为零,如果已经为零,则不可能。
情况2:初始Nim Sum不为零
现在通过最佳方法A将使Nim Sum现在为零(如上所述,这是可能的,因为初始Nim总和为非零)。现在,在B“轮到作为NIM总和已经是零时什么号B选秀权,净息差总和将是非零和A可以选择一个号码,使净息差和零一次。只要有任何可用的物品堆,这将继续进行。
A将是选择最后一项的人。
因此,正如上述情况所讨论的,对于任何玩家来说,显而易见的最佳策略是使尼姆总和为零(如果非零),并且如果该数字已经为零,那么无论玩家现在采取什么行动,都可以对其进行反击。 。
让我们在以上玩的游戏中应用以上定理。在第一个游戏中, A首先开始,而游戏开始时的Nim-Sum为3 XOR 4 XOR 5 = 2,这是一个非零值,因此A获胜。而在第二局中,当桩的初始配置分别为1、4、5和A时,则A注定要输,因为游戏开始时的Nim-Sum是1 XOR 4 XOR 5 = 0。
执行:
在下面的程序中,我们在计算机和人(用户)之间玩Nim游戏
下面的程序使用两个功能
knowWinnerBeforePlaying()::在播放前告知结果。
playGame():玩完整的游戏,最后宣布获胜者。函数playGame()不接受人类(用户)的输入,而是使用rand()函数随机拾取一堆并从拾取的堆中随机除去任意数量的石头。
通过删除rand()函数并插入cin或scanf()函数,可以将以下程序修改为从用户处获取输入。
C++
/* A C++ program to implement Game of Nim. The program
assumes that both players are playing optimally */
#include
#include
using namespace std;
#define COMPUTER 1
#define HUMAN 2
/* A Structure to hold the two parameters of a move
A move has two parameters-
1) pile_index = The index of pile from which stone is
going to be removed
2) stones_removed = Number of stones removed from the
pile indexed = pile_index */
struct move
{
int pile_index;
int stones_removed;
};
/*
piles[] -> Array having the initial count of stones/coins
in each piles before the game has started.
n -> Number of piles
The piles[] are having 0-based indexing*/
// A C function to output the current game state.
void showPiles (int piles[], int n)
{
int i;
cout <<"Current Game Status -> ";
for (i=0; i 0)
non_zero_indices [count++] = i;
(*moves).pile_index = (rand() % (count));
(*moves).stones_removed =
1 + (rand() % (piles[(*moves).pile_index]));
piles[(*moves).pile_index] =
piles[(*moves).pile_index] - (*moves).stones_removed;
if (piles[(*moves).pile_index] < 0)
piles[(*moves).pile_index]=0;
}
return;
}
// A C function to play the Game of Nim
void playGame(int piles[], int n, int whoseTurn)
{
cout <<"\nGAME STARTS\n\n";
struct move moves;
while (gameOver (piles, n) == false)
{
showPiles(piles, n);
makeMove(piles, n, &moves);
if (whoseTurn == COMPUTER)
{
cout <<"COMPUTER removes" << moves.stones_removed << "stones from pile at index "
<< moves.pile_index << endl;
whoseTurn = HUMAN;
}
else
{
cout <<"HUMAN removes"<< moves.stones_removed << "stones from pile at index "
<< moves.pile_index << endl;
whoseTurn = COMPUTER;
}
}
showPiles(piles, n);
declareWinner(whoseTurn);
return;
}
void knowWinnerBeforePlaying(int piles[], int n,
int whoseTurn)
{
cout <<"Prediction before playing the game -> ";
if (calculateNimSum(piles, n) !=0)
{
if (whoseTurn == COMPUTER)
cout <<"COMPUTER will win\n";
else
cout <<"HUMAN will win\n";
}
else
{
if (whoseTurn == COMPUTER)
cout <<"HUMAN will win\n";
else
cout <<"COMPUTER will win\n";
}
return;
}
// Driver program to test above functions
int main()
{
// Test Case 1
int piles[] = {3, 4, 5};
int n = sizeof(piles)/sizeof(piles[0]);
// We will predict the results before playing
// The COMPUTER starts first
knowWinnerBeforePlaying(piles, n, COMPUTER);
// Let us play the game with COMPUTER starting first
// and check whether our prediction was right or not
playGame(piles, n, COMPUTER);
/*
Test Case 2
int piles[] = {3, 4, 7};
int n = sizeof(piles)/sizeof(piles[0]);
// We will predict the results before playing
// The HUMAN(You) starts first
knowWinnerBeforePlaying (piles, n, COMPUTER);
// Let us play the game with COMPUTER starting first
// and check whether our prediction was right or not
playGame (piles, n, HUMAN); */
return(0);
}
// This code is contributed by shivanisinghss2110
C
/* A C program to implement Game of Nim. The program
assumes that both players are playing optimally */
#include
#include
#include
#define COMPUTER 1
#define HUMAN 2
/* A Structure to hold the two parameters of a move
A move has two parameters-
1) pile_index = The index of pile from which stone is
going to be removed
2) stones_removed = Number of stones removed from the
pile indexed = pile_index */
struct move
{
int pile_index;
int stones_removed;
};
/*
piles[] -> Array having the initial count of stones/coins
in each piles before the game has started.
n -> Number of piles
The piles[] are having 0-based indexing*/
// A C function to output the current game state.
void showPiles (int piles[], int n)
{
int i;
printf ("Current Game Status -> ");
for (i=0; i 0)
non_zero_indices [count++] = i;
(*moves).pile_index = (rand() % (count));
(*moves).stones_removed =
1 + (rand() % (piles[(*moves).pile_index]));
piles[(*moves).pile_index] =
piles[(*moves).pile_index] - (*moves).stones_removed;
if (piles[(*moves).pile_index] < 0)
piles[(*moves).pile_index]=0;
}
return;
}
// A C function to play the Game of Nim
void playGame(int piles[], int n, int whoseTurn)
{
printf("\nGAME STARTS\n\n");
struct move moves;
while (gameOver (piles, n) == false)
{
showPiles(piles, n);
makeMove(piles, n, &moves);
if (whoseTurn == COMPUTER)
{
printf("COMPUTER removes %d stones from pile "
"at index %d\n", moves.stones_removed,
moves.pile_index);
whoseTurn = HUMAN;
}
else
{
printf("HUMAN removes %d stones from pile at "
"index %d\n", moves.stones_removed,
moves.pile_index);
whoseTurn = COMPUTER;
}
}
showPiles(piles, n);
declareWinner(whoseTurn);
return;
}
void knowWinnerBeforePlaying(int piles[], int n,
int whoseTurn)
{
printf("Prediction before playing the game -> ");
if (calculateNimSum(piles, n) !=0)
{
if (whoseTurn == COMPUTER)
printf("COMPUTER will win\n");
else
printf("HUMAN will win\n");
}
else
{
if (whoseTurn == COMPUTER)
printf("HUMAN will win\n");
else
printf("COMPUTER will win\n");
}
return;
}
// Driver program to test above functions
int main()
{
// Test Case 1
int piles[] = {3, 4, 5};
int n = sizeof(piles)/sizeof(piles[0]);
// We will predict the results before playing
// The COMPUTER starts first
knowWinnerBeforePlaying(piles, n, COMPUTER);
// Let us play the game with COMPUTER starting first
// and check whether our prediction was right or not
playGame(piles, n, COMPUTER);
/*
Test Case 2
int piles[] = {3, 4, 7};
int n = sizeof(piles)/sizeof(piles[0]);
// We will predict the results before playing
// The HUMAN(You) starts first
knowWinnerBeforePlaying (piles, n, COMPUTER);
// Let us play the game with COMPUTER starting first
// and check whether our prediction was right or not
playGame (piles, n, HUMAN); */
return(0);
}
输出:在不同的运行中可能会有所不同,因为随机数用于决定下一步(对于松散的玩家)。
Prediction before playing the game -> COMPUTER will win
GAME STARTS
Current Game Status -> 3 4 5
COMPUTER removes 2 stones from pile at index 0
Current Game Status -> 1 4 5
HUMAN removes 3 stones from pile at index 1
Current Game Status -> 1 1 5
COMPUTER removes 5 stones from pile at index 2
Current Game Status -> 1 1 0
HUMAN removes 1 stones from pile at index 1
Current Game Status -> 1 0 0
COMPUTER removes 1 stones from pile at index 0
Current Game Status -> 0 0 0
COMPUTER won
参考 :
https://zh.wikipedia.org/wiki/尼姆