Nim與取火柴問題

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-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的證明來的。

實例

例1:2. 取火柴遊戲的規則如下:一堆火柴有N根,A、B兩人輪流取出。每人每次可以取1 根或2 根,最先沒有火柴可取的人爲敗方,另一方爲勝方。如果先取者有必勝策略則記爲1,先取者沒有必勝策略記爲0。
當N 分別爲100,200,300,400,500 時,先取者有無必勝策略的標記順序爲(回答應爲一個由0 和/或1 組成
的字符串)。
第一題比較簡單,可以推廣到有n根火柴每人可取1到m根
若n % ( m + 1 ) == 1則先取必敗,否則必勝,這個比較好證明
很簡單可以證明當火柴數爲1時先取必敗,然後火柴數爲m + 1時,無論先取的人怎樣取,假定取x個,那麼第二個人取m - x個,即可只剩一個留給對方。這樣一直下來,如果用D( x ) = 1表示當留有x個火柴時先取必敗,那麼
D( 1 ) = 1
D( m + 2 ) = 1
D( 2m + 3 ) = 1
........
在這道題的情況就是
D( 1 ) = 1
D( 4 ) = 1
D( 7 ) = 1
.........
所以剛開始這些情況是必敗的,換言之,如果剛開始不是這種情況,則先取的人可以取n % ( m + 1 ) - 1個(注意n % ( m + 1 ) == 0的情況,這是應該是取m個),就可以轉化成上述令對手必敗的情況。
例2:2.(取石子游戲)現有5堆石子,石子數依次爲3,5,7,19,50,甲乙兩人輪流從任一堆中任取(每次只能取自一堆,不能不取),取最後一顆石子的一方獲勝.甲先取,問甲有沒有獲勝策略(即無論乙怎樣取,甲只要不失誤,都能獲勝) 如果有,甲第一步應該在哪一堆裏取多少 請寫出你的結果
只有一堆時,無論有多少,先取者都可以一次性全部取走,所以必勝。

(1,1)時,顯然先取者必敗。
(1,2)時,先取者必勝,他可以在2那一堆中取1個,於是變成(1,1),但這成爲上一種情況了,於是接下來取的人必敗,亦即先取者必勝。
(1,3)時,先取者必勝。他可以在3那一堆中取2個,於是變成(1,1)。
(2,2)時,先取者必敗。他在任何一堆中取1個,對方隨即在另一堆中取1個,即變成(1,1);如果他取走一堆中的全部石子,對方即取走另一堆中的全部石子。
(2,3)時,先取者必勝。他可以在3那一堆中取1個,於是變成(2,2)。
(3,3)時,先取者必敗。他取走任一堆中的1,2或3個,就變成了以上討論過的情形。

(1,1,1)時,先取者必勝。他取走任一堆,就變成了(1,1)。
(1,1,2)時,先取者必勝。他取走2那一堆,就變成了(1,1)。
(1,1,3)時,先取者必勝。他取走3那一堆,就變成了(1,1)。
(1,2,2)時,先取者必勝。他取走1那一堆,就變成了(2,2)。
(1,2,3)時,先取者必敗。分析如下:
   他先取1那一堆,則變爲(2,3),由上面的分析,對手必勝。
   他從2那一堆中取1個,就變成了(1,1,3),對手可以將3那一堆全部取走,變成了(1,1),於是必勝。
   他將2那一堆全部取走,就變成了(1,3),對手必勝。
   他從3那一堆中取1個,就變成了(1,2,2),對手必勝。
   他從3那一堆中取2個,就變成了(1,2,1),對手必勝。
   他將3那一堆全部取走,就變成了(1,2),對手必勝。

  這些勝負有什麼規律呢?我們可以將每堆的數轉換成二進制,然後看每一位上所有堆裏的1的個數總和:
  必勝情況:(n) (1,2)(1,3)(2,3) (1,1,1)(1,1,2)(1,2,2)
  必敗情況: (1,1)(2,2)(3,3) (1,2,3)

化爲二進制:
必勝情況:
(n)<只有1堆>:……(反正每位只要有1肯定只有1個)
(1,2):1,10
  列成豎式:
  01
  10
  個位上只有1個1,“十位”(因爲是二進制所以叫十位不妥,這裏爲了方便說明暫且使用,下同)上也只有1個1。
(1,3):1,11
  列成豎式:
  01
  11
  個位上有2個1(1的1個,3的1個),十位上有1個1。
(2,3):10,11
  個位上有1個1,十位上有2個1。
(1,1,1):1,1,1
  個位上有3個1。
(1,1,2):1,1,10
  個位上有2個1,十位上有1個1。
(1,1,3):1,1,11
  個位上有3個1,十位上有1個1。
(1,2,2):1,10,10
  個位上有1個1,十位上有2個1。

必敗情況:
(1,1):1,1
  個位上有2個1。
(2,2):10,10
  十位上有2個1。
(3,3):11,11
  個位上有2個1,十位上也有2個1。
(1,2,3):1,10,11
  個位上有2個1,十位上也有2個1。

  下面分析一下這些情況。
  先看必敗情形。容易發現,所有的必敗情形,都是所有的數位上都有偶數個1。
  下看必勝情形。我們發現,出現了兩種情況:
  1.只有1位上有奇數個1,如(1,3)(2,3)(1,1,1)(1,1,2)(1,2,2)。而先取者取走該位上的1,所有的位上就都變成了偶數個1,而這時後取者變成了先取者。
  2.有若干位上都是奇數個1,如(n)(1,2)(1,1,3)。先取者取(不一定取走哪位)後,所有的位上也都變成了偶數個1。後取者變成了先取者。
  以上兩種情況,都是將後取者逼至必敗情況從而取勝。

  由以上分析我們可以得到結論:將所有的堆的石子數化爲二進制後,如果所有數位上的1的個數都是偶數,那麼先取者必敗;如果有些位上的1的個數是奇數,先取者能夠將所有數位上的1的個數都變爲偶數的話,那麼先取者必勝。

好,下面來分析我們的題目。
3,5,7,19,50化爲二進制是:
000011
000101
000111
010011
110010
可見,只有最高位的1是奇數個,其他位上都是偶數個。
所以只需要將最高位的1取走即可必勝。
二進制的100000就是10進制的32,所以要將50個石子的那堆取32個,取掉就變成偶數個數目。於是先取者必勝。以後無論對方怎麼取,始終保證每一位上的1的個數是偶數即可(一種簡單的方法是,他在一堆中取幾個,你在另一堆中也取幾個就可以)。
例3:有n根火柴,甲乙兩人輪流從中拿取,一次至少拿一根,至多拿先前對方一次所取火柴數目的兩倍。甲先拿,開始時甲可以拿任意數目的火柴(不能拿完)。最先沒有火柴拿的一方爲敗方。給定一個n,問甲能否贏!(1<n<=10^9)
是斐波那契數列,如果是這個數列中的數,先手輸,否則先手贏。
贏的方法就是拿掉若干根,使之成爲斐波那契數列中對應的項(最接近的一項)。
定義函數f(n, m)表示當前有n根火柴,且對手上一步取了m根的情況下,有沒有必勝法,若有f=1,否則f=0
若是第一次走,令m=0,其他情況m都不爲0。 n是從2開始的,爲了敘述方便,補充n=0和1時的定義: f(0, m)=0, f(1, m)=1 m>=1。無需定義f(1,0)和f(0,0)
對一個固定的n,必須是f(n,1), f(n,2), ..., f(n,ceiling(n/2)-1)都爲0, 則f(n,0)才爲0。否則f(n,0)=1。
n=2時 f(2,m)=1 m>=1 得出f(2,0)=0. 表示若共有2根火柴,先走的人必敗,若是當前有2根火柴,且對手上一次取了1根或以上,當前先走的人必勝
依次類推:
f(3,1)=0, f(3,m)=1 m>=2 則f(3,0)=0  
f(4,m)=1 m>=0 則f(4,0)=1
……………………
算法是:f(n,m)=f(n-1,1) | f(n-2, 2) | ... | f(n-2m,2m) 然後取反

發現一旦f(n,m)=1, 那麼右邊的當n不變m增大時f的值都爲1
推斷f(n,0)=0的序列是Fibonacci數列
用歸納法來證明,假設n是一個Fibonacci數
有f(n, 1)=…=f(n, ceiling(n/2)-1)=0, f(n, ceiling(n/2))=1
我們發現f(n+1,m)=f(1,m), f(n+2,m)=f(2,m)......對任何m成立,也就是說f的值從頭重複了一遍
但是f(n+1,0), f(n+2,0), ...都是1,因爲對一個固定的n,必須是f(n,1), f(n,2), ..., f(n,ceiling(n/2)-1)都爲0, 則f(n,0)才爲0
第一次例外是f(n+k),其中k也是一個Fibonacci數,滿足k>n/2
而滿足這個條件的第一個k就是n之前的那個Fibonacci數
所以n+k是n之後的第一個Fibonacci數
發佈了22 篇原創文章 · 獲贊 6 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章