1. 程式人生 > >Nim 博弈遊戲詳解

Nim 博弈遊戲詳解

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):N= as…a1a0N= bs…b1b0……N= ms…m1m0如果每一種大小的子堆的個數都是偶數,我們就稱Nim取子游戲是平衡的,而對應位相加是偶數的稱為平衡位,否則稱為非平衡位。因此,Nim取子游戲是平衡的,當且僅當:

a+ bs + … + ms 是偶數

……

a+ b+ … + m是偶數

a+ 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的堆0111
大小為9的堆1001
大小為12的堆1100
大小為15的堆1111
由Nim取子游戲的平衡條件可知,此遊戲是一個非平衡狀態的取子游戲,因此,遊戲人I在按獲勝策略進行取子游戲下將一定能夠取得最終的勝利。具體做法有多種,遊戲人I可以從大小為12的堆中取走11枚硬幣,使得遊戲達到平衡(如下表),

23 = 8

22 = 4

21 = 2

20 = 1

大小為7的堆0111
大小為9的堆1001
大小為12的堆0001
大小為15的堆1111
之後,無論遊戲人II如何取子,遊戲人I在取子後仍使得遊戲達到平衡。同樣的道理,遊戲人I也可以選擇大小為9的堆並取走5枚硬幣而剩下4枚,或者,遊戲人I從大小為15的堆中取走13枚而留下2枚。

歸根結底,Nim取子游戲的關鍵在於遊戲開始時遊戲處於何種狀態(平衡或非平衡)和第一個遊戲人是否能夠按照取子游戲的獲勝策略來進行遊戲。

例題:51Nod1069

有N堆石子。A B兩個人輪流拿,A先拿。每次只能從一堆中取若干個,可將一堆全取走,但不可不取,拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出N及每堆石子的數量,問最後誰能贏得比賽。例如:3堆石子,每堆1顆。A拿1顆,B拿1顆,此時還剩1堆,所以A可以拿到最後1顆石子。Input
第1行:一個數N,表示有N堆石子。(1 <= N <= 1000)
第2 - N + 1行:N堆石子的數量。(1 <= A[i] <= 10^9)
Output
如果A獲勝輸出A,如果B獲勝輸出B。
Input示例
3
1
1
1
Output示例
A

#include <stdio.h>
int main() {
	int n, i, x, f;
	f = 0;
	scanf("%d", &n);
	for(i = 0; i < n; i++) {
		scanf("%d", &x);
		f ^= x;
	}
	if(f==0) {
		printf("B\n");
	} else {
		printf("A\n");
	}
	return 0;
}