歡迎光臨
每天分享高質量文章

詳解博弈論及演演算法實現

(點選上方公眾號,可快速關註)

轉自:Keambar

https://www.cnblogs.com/keam37/p/3403695.html

好文投稿, 請點選 → 這裡瞭解詳情

 

在生活中五子棋也是一種先手有必贏策略的遊戲,有人會說五子棋先手我也會輸啊,所以

博弈論問題都有個類似如“參與者足夠聰明”,“兩人都不犯錯”的前提。

在此前提下,討論幾種常見的博弈情形。  

一、巴什博弈(Bash Game)

只有一堆n個物品,兩個人從輪流中取出(1~m)個;最後取光者勝。

考慮到 若n=m+1 那麼 第一個人不論如何取都不能取勝。

進一步我們發現 若 n=k*(m+1)+r; 先取者拿走 r 個,那麼後者再拿(1~m)個

      

n=(k-1)*(m+1)+s; 先取者再拿走s 個 最後總能造成 剩下n=m+1 的局面。

      

因此,此時先手有必贏策略。

      

相對應的,若n=k*(m+1) 那麼先取者必輸。

      

因此我們可以寫出對應的程式(預設 n m都大於0)

int Bash_Game(int n,int m)

//是否先手有必贏策略

{

   if (n%(m+1)!=0) return 1;

   return 0;

}

      

到了這,不如嘗試一下當有N堆,每堆有Mi>0個物品,依舊是兩個人來取該怎麼判斷?

     

先考慮取的最大數目無上限即可以把一堆全部取完的情形

二、尼姆博弈(Nimm Game)

正如上述.

      

從巴什博弈我們知道一個當情形對應一種狀態,而由一個狀態只能變為另一種狀態時能很輕易地判斷是否先手有必贏策略

      

那麼怎麼樣才能在尼姆博弈裡找到這樣的狀態呢?

      

如果把n堆抽象為n個非負整數,再將n個整數轉化為二進位制,然後對n個二進位制數按位相加(不進位),若每一位相加都為偶數,

      

那麼稱這個狀態為偶狀態,否則稱它為奇狀態.

      

可以證明:任何一個偶狀態在其中一個數變小後一定成為奇狀態,而一個奇狀態一定可以透過改變一個數變成偶狀態.

      

前一點很顯然,因為一個數變小至少有一位發生改變,這一位就改變了原來的偶狀態.

      

對於後一點,對於一個從高位到低位某一位和為奇的奇狀態,必定有一個數的二進製表示在此位為1,對於後面的較低位和為奇的情況,只要把這個數對應位取反即可得到一個偶狀態.

 

到此,成功的構造了兩個可以轉換的狀態!!!

      

那麼對於n堆物品,只要判斷它是否是奇狀態就可以判斷是否先手有必贏策略.

      

但是求每個數的二進製表示略顯麻煩,可以用更好的辦法,也是我偏愛的位運算.

      

XOR 和判斷:

      

如果有奇數個二進位制數在第K位為1 那麼在這一位上的和為奇,同樣的,偶數個1和為偶.

      

很明顯位運算xor滿足我們的要求,偶數個1異或和為0,奇數個為1;

      

由此,終於可以給出演演算法

int Nimm_Game(int n)//假設n個數存在陣列f[]中,有必勝策略傳回1

{

    int flag=0;

    for(int i=1;i<=n;i++)

    flag^=f[i];

    if(flag) return 1;

    return 0;

}

但是我遇到過n非常大,且每一堆的物品數為連續的整數的情況

  

對此我們要考慮連續非負整數的異或和問題 

  

記 f(x, y) 為x到y的所有整數的異或值。 f[1,n]=f[0,n];

  

當n為2^k-1(2的K次方減一)時;

  

0 到 2^k-1 共2^k個數 等於∑C(n,i)

  

可以看做在k個位置中放入i個0,最後求和

  

同時可以看做在空格位置中放入i個1;最後求和

  

即在每一位上1個0的個數都相等,每個位上有2^(k-1)個1,當k>=2時 1的個數為偶數;

  

而我們已經知道偶數個1的異或和為0

所以 f[0, 2^k – 1] = 0 (k >= 2)

 

對 f[0, n]  (n>=4) 設n的最高位1是在第k位(k >= 2),

f[0, n] = f[0, 2^k – 1] xor f[2^k, n] = f[2^k, n]

對2^k到n這n+1-2^k個數,最高位(第k位)共有 m = n+1-2^k 個1,

 

2^k總是偶數,因此,當n為奇數時,m是偶數,f[0, n] = f[2^k, n] = f[0, n – 2^k]

當n為偶數(n – 2^k)總是偶數 ,所以:f[0,n] = f[0,n-2^k-2^(k-1)…-2^2](只到3是因為k>=2)

此時只剩下兩位是我們需要的 我們可以用(n & 3)很快得到後兩位

由於n是偶數 所以(n & 3)只可能得到 1 或 3;

1對應 二進位制數 (01)所以是奇數個1  此時f [0,n]=1;

3對應 二進位制數 (11) 此時f[0,n]=0;

 

當n為偶數時,m是奇數,因而 f[0, n] = f[2^k, n] = f[0, n – 2^k]  xor  2^k

可得:f[0, n] =  f(0, n & 3) xor 2^k xor n[k]*2^(k-1) xor ….n[2]*2^2 n[k] 為 n的二進位制數的第k位;

很明顯 當n為偶數時 f[0,n]的二進位制從最高位到第3位(如果不止3位) 跟n的二進位制數從高位到第三位 相同;

此時只需要 判斷 第二位

n & 3=0對應後二位為(00) 此時 f[0,n]=n;

n & 3=2對應後二位為(10) 此時 f[0,n]=n+1;

 

綜上所述:

                               n        n % 4 == 0

  f[1, n]  =  f[0, n]  = 1        n % 4 == 1

                               n +1   n % 4 == 2

                               0        n % 4 == 3

  f[x,y] = f[0,y] xor f[0,x-1] .(x>0)

 

對應的程式碼在這

//讀入n,表示有從物品數分別1到n的n堆物品,假設n個數存在陣列f[]中

int xor_n(int n)//從1到n的異或和

{

     int t = n & 3;

     if (t & 1) return t / 2 ^ 1;

     return t / 2 ^ n;

}

int Nimm_Game(int n)//有必勝策略傳回1

{

    int flag=0;

    for(int i=1;i<=n;i++)

    flag^=xor_n(f[i]);

    if(flag) return 1;

    return 0;

}



覺得本文有幫助?請分享給更多人

關註「演演算法愛好者」,修煉程式設計內功

淘口令複製以下紅色內容,再開啟手淘即可購買

範品社,使用¥極客T恤¥搶先預覽(長按複製整段文案,開啟手機淘寶即可進入活動內容)

近期,北京地區正常發貨,但派件時間有所延長。

贊(0)

分享創造快樂