1. 程式人生 > 其它 >淺談博弈論中的經典模型和SG函式

淺談博弈論中的經典模型和SG函式

前言:

在本章會提到:

1.巴什博弈

2.威佐夫博弈

3.Nim博弈

4.斐波那契博弈

5.SG函式

這些博弈我只給出結論,證明的話網上一大把,請自行檢視.

因為作者本身比較菜,接觸的博弈論的題比較少,所以講的可能會有點通俗,不會很嚴謹.

話說之前在哪看到還有種博弈是在n堆石子中每次最多可以從k堆石子中取石子,如果有大佬知道,可以@我一下.

一.巴什博弈(Bash Game)

A和B一塊報數,每人每次報最少a個,最多報b個,看誰先報到n。這應該是最古老的關於巴什博奕的遊戲了吧。

如果n%(a+b)=0,那麼後手必勝.否則先手必勝.

話說這個不是小學奧數的題.

二.威佐夫博弈(Wythoff Game)

有兩堆各若干的物品,兩人輪流從其中一堆取至少一件物品,至多不限,或從兩堆中同時取相同件物品,規定最後取完者勝利。

假如一開始兩堆的物品為a,b.(a<b)

那麼如果(int)(b-a)*w=a 先手必輸,否則必敗.

w=(sqrt(5)+1)/2,也就是約等於1.618,是不是很像黃金比例,這就是博弈的魅力.

#include<bits/stdc++.h>
using namespace std;
int main()
{
    long long a,b;
    cin>>a>>b;
    if(a>b) swap(a,b);
    long
long k=abs(b-a); if((int)((sqrt(5.0)+1.0)/2.0*k)==a) cout<<0; else cout<<1; return 0; }

三.尼姆博弈(Nimm Game)

尼姆博弈指的是這樣一個博弈遊戲:有任意堆物品,每堆物品的個數是任意的,雙方輪流從中取物品,每一次只能從一堆物品中取部分或全部物品,最少取一件,取到最後一件物品的人獲勝。

結論就是:把每堆物品數全部異或起來,如果得到的值為0,那麼先手必敗,否則先手必勝。

四.斐波那契博弈

有一堆物品,兩人輪流取物品,先手最少取一個,至多無上限,但不能把物品取完,之後每次取的物品數不能超過上一次取的物品數的二倍且至少為一件,取走最後一件物品的人獲勝。

結論是:先手勝當且僅當n不是斐波那契數(n為物品總數)

五.SG函式

有向圖遊戲:

給定一個有向無環圖,圖中有一個唯一的起點,在起點上放一個棋子.兩名玩家交替的把這枚棋子向有向邊移,每次可以移動移動一步,無法移動者為負.該遊戲被稱為有向圖遊戲.任何一個公平組合遊戲都可以轉換為有向圖遊戲.具體方法是,把每個局面看成圖中的一個節點,並且從每個局面向沿著合法行動能夠到達的下一個局面連有向邊.

Mex運算

設S表示一個非負整數集合.定義mex(S)為求出不屬於集合S的最小非負整數數.

SG函式

在有向圖遊戲中,對於每個節點x,SG(x)=mex({SG(y1),SG(y2).....}),其中y為x的後繼節點.特別的,整個有向圖遊戲G的SG函式值被定義為有向圖起點s的SG函式,即SG(G)=SG(s).

有向圖遊戲的和

設G1.G2,G3....為m個有向圖遊戲,定義有向圖遊戲G,它的行動規則為任選一個Gi並在Gi上行動一步,G被稱為有向圖遊戲G1,G2,G3.....的和.

SG(G)=SG(G1)^SG(G2)^SG(G3).....^SG(Gm)

定理:

有向圖遊戲的某個局面必勝,當且僅當該局面對應的節點SG函式大於0

有向圖遊戲的某個局面必敗,當且僅當該局面對應的節點SG函式等於0

一般將終止狀態SG(T)=0;也就是必敗局面.想必瞭解完上面的知識後,可以發現nim博弈就可以轉換為SG函式.

下面給出一道例題,以更近距離的感受一下SG函式

首先我們發現:

SG(x,y)=mex{SG(x-y,y),SG(x-2y,y).....SG(y,x%y)}

SG(x-y,y)=max(SG(x-2y,y)...SG(y,x%y))

也就是SG(x,y)是可以由SG(y,x%y)推出來.

我們來討論

SG(y,x%y)=1時,則SG()推上去就是0,2,3,4.....所以SG(x,y)>0,即必勝局面

SG(y,x%y)=0時,則SG()推上去就是1,2,3,4.....所以SG(x,y)>0,即必勝局面

所以當x/y=1時,SG(x,y)=!SG(y,x%y)

所以就是個遞迴啦,當y=0時,return 0(必敗)

還是放個程式碼把:

#include<bits/stdc++.h>
using namespace std;
int T, m, n;
bool solve(int n, int m)
{
    if (!m)return false;
    if (n/m == 1)return !solve(m, n%m);
    else return true;
}
int main()
{
    cin>>T;
    for (int i = 1; i <= T; i++)
    {
        cin>>n>>m;
        if (solve(max(n, m), min(n, m))) printf("Stan wins\n");
        else printf("Ollie wins\n");
    }
}

總結:

講實話我對博弈論的題目不太熟,感覺好多腦筋急轉彎?SG函式的應用我也寫的很少,如果以後有機會進行深入研究的話,我再來補充.