初亏博弈门道
2015-08-25 13:13
190 查看
The Sprague-Grundy theory of impartial games
公平游戏的Sprague-Grundy定理
公平游戏是一种双人游戏,在游戏中双方都有完整的信息,没有牵涉,任何状态的合法操作对双方来说都是相同的。
一个公平游戏可以抽象地用一个有向无环图来表示,这个图中每个点都对应这一个状态,每条有向边代表从一个状态到另一个状态的合法操作。
我们可以想象一个代币最初放在某个点上,然后两个玩家轮流将其从当前的点移动到它的后继点。当代币移动到汇点时游戏结束,汇点是一个没有出度的点,最后一个需要操作的玩家就是胜者。
P- 和 N-状态
如果双方都按照最佳策略进行游戏,我们可以将游戏中的每个状态依据其是先手必胜还是后手必胜分类。
一个先手胜状态被认为是一个N-状态(因为下一个玩家即将获胜),一个后手胜状态被认为是一个P-状态(因为前一个玩家即将获胜)
P-和N-状态归纳性地描述如下:
一个点v是P-状态当且仅当它的所有后继都为N-状态
一个点v是N-状态当且仅当它的一些后继是P-状态
这个归纳从汇点开始,汇点是P-状态因为它显然满足P-状态的要求。
游戏的P-和N-状态的信息提供了它的必胜策略。如果轮到我们且游戏处在一个N-状态,我们应该转移到一个P-状态。接着我们的对手就会被迫进入N-状态,依此类推。我们最终会移入一个汇点并获得胜利。
游戏的和
如果G1和G2 是公平游戏,那么他们的和G1 + G2是另一个公平游戏,玩法如下:每个回合,一个玩家选择G1, G2 中的一个(随便哪个他希望的)然后玩它,不碰另一个游戏。当 G1 和 G2都不能操作时游戏结束。
形式上,如果 G1 = (V1, E1) 和 G2 = (V2, E2)是游戏图,那么他们的和 Gsum = (Vsum, Esum) 规定为:
Vsum = V1 × V2,
Esum = {(v1v2, w1v2) | (v1, w1) ∈ E1} ∪ {(v1v2, v1w2) | (v2, w2) ∈ E2}.
现在,假定我们给出两个游戏G1 和 G2。如果我们只知道单个游戏的P-状态和N-状态我们能够正确地玩好游戏和G1 + G2吗?答案是否定的。不难看出两个P-状态的和总是P-状态,P-状态和N-状态的和总是N-状态。但是两个N-状态的和既可能是P-状态也可能是N-状态。因此,只知道单个游戏的P-状态和N-状态是不够的。
为了正确地玩好游戏和我们需要推广P-状态和N-状态,它就是Sprague-Grudy函数(或者简称为Grundy函数)。
The Sprague-Grundy function
Sprague-Grundy 函数
令N = {0, 1, 2, 3, …} 为自然数的集合。Sprague-Grundy 函数给游戏中的每个状态分配了一个自然数。结点v的Grundy值等于没有在v的后继的Grundy值中出现的最小自然数。
形式上:给定一个有限子集 S ⊂ N,令mex S(最小排斥值)为没有出现在S中的最小自然数
mex S = min (N S).
现在,给定一个游戏图G=(V,E),其Sprague-Grundy函数g:V → N 归纳定义为
g(v) = mex {g(w) | (v, w) ∈ E}.
从G的汇点开始归纳,可知它的Grundy值为0
Sprague-Grundy函数满足两个重要性质:
点v是一个P-状态当且仅当g(v)=0
如果G = G1 + G2 且 v = v1v2 是G的一个状态,那么g(v) 为g(v1) 和 g(v2) 在二进制下的异或:
g(v) = g(v1) ⊕ g(v2).
运算⊕也称作nim和。举个例子,3 ⊕ 5 = 011 ⊕ 101 = 110 = 6。类似地,3 ⊕ 6 = 5 且 5 ⊕ 6 = 3。
不难利用归纳法证明上面两个性质。
根据这些性质有v = v1v2 是P-状态当且仅当g(v1) = g(v2), 因为这是唯一能够使得nim和为0的途径。
无疑,游戏的求和是满足交换律和结合律的运算,nim和运算也是。
因此,我们可以通过获知单个游戏的Grundy函数来正确地玩好任意数目游戏和。
我们的策略如下:如果轮到我们且游戏的Grundy值给出了一个非0的nim和,那么必然在游戏的某个组分中存在一个操作使得nim和变为0。我们应该执行这个操作,那么接着我们的对手就被迫再次使得nim和非0。最终,我们将成为在最后一个游戏执行最后一个操作的人,最后将nim和变为0.
The game of Nim
Nim游戏
最基本的公平游戏是Nim堆。一个Nim堆由确定数目代币组成。在每个回合,一个玩家从堆上拿走1到整堆中任意数目的代币。拿空整堆的人获得胜利。
这个游戏如果独立看是没有意义的:先手玩家可直接拿走所有代币并立即获得胜利!
但是如果我们将各种大小的Nim堆加在一起,我们就得到了著名的Nim游戏。
大小为n的Nim堆的Grundy值为n。因此,Nim游戏中每个状态的Grundy值为每堆大小的Nim和。
Games that decompose into sums of themselves
一些分解成自身和的游戏
Sprague-Grundy定理最自然的应用就是一些分解成自身和的一些游戏。
考虑下面这个游戏:有一个大小为m*n的棋盘,且有无限数目某特定形状的骨牌供应。在每个回合,玩家在棋盘上一个空位放置一个骨牌,不能放骨牌的玩家就是败者。
在游戏期间,棋盘会逐渐分成不同的区域,对其我们可以分别计算Grundy值。
再举个例子,考虑Grundy游戏。这个游戏的一个状态由一些不同大小的代币堆组成,一次操作由只取一堆并把它分成两个不相等的堆组成。当所有堆的大小只有1和2的时候游戏结束,因为它不能再分。
令g(n)为单个大小为n的堆的Grundy值。数列g(n)如下:
n: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20…
g(n):0 0 1 0 2 1 0 2 1 0 2 1 3 2 1 3 2 4 3 0
比如:
当n等于1,2时已满足条件,即不能再取,也就没有下一个局面,所以g(1)={};所以G(1)={0,1,2,3,4…};
所以g(1)=0;同理g(2)=0;依次递推,g(3),g(4),g(5)等,
例如:g(6)={#(1,5),#(2,4)}={g(1)+g(5),g(2)+g(4)}=g(2,0);
所以G(6)={1,3,4,5,6…},所以g(6)=1;
甲乙两人面对若干排石子,其中每一排石子的数目可以任意确定。例如图所示的初始局面:共n=3排,其中第一排的石子数a1=7,第二排石子数a2=3,第三排石子数a3=3。两人轮流按下列规则取走一些石子,游戏的规则如下:每一步必须从某一排中取走两枚石子;这两枚石子必须是紧紧挨着的;如果谁无法按规则取子,谁就是输家。
解:
用符号#S,表示局面S所对应的二进制数。
用符号$(x),表示局面(x)的下一步所有可能出现的局面的集合。
定义集合g(x):设$(x)={S1, S2, …, Sk},则g(x)={#S1, #S2, …, #Sk}。
函数f满足要求的一个充分条件
f(a1)不属于集合g(a1)。
集合g(a1)包含集合{0, 1, …, f(a1)–1}。
如果g(a1)={0, 1, 2, 5, 7, 8, 9},则f(a1)=3,满足要求。
用大写字母N表示非负整数集,即N={0, 1, 2, …}。
令N为全集,集合G(x)表示集合g(x)的补集。
定义函数f(n):f(n)=min{G(n)},即f(n)等于集合G(n)中的最小数。
设局面S=(a1, a2, …, an),#S=f(a1)+f(a2)+…+f(an),采用二进制数的加法。
若#S=0,则S负;若#S≠0,则S胜。
游戏C的f值:
g(0)={},G(0)={0, 1, …},f(0)=0;
g(1)={},G(1)={0, 1, …},f(1)=0;
g(2)={#(0)}={f(0)}={0},G(2)={1, 2, …},f(2)=1;
g(3)={#(1)}={f(1)}={0},G(2)={1, 2, …},f(3)=1;
g(4)={#(2), #(1, 1)}={f(2), f(1)+f(1)}={1, 0},G(4)={2, 3, …},f(4)=2;
g(5)={#(3), #(1, 2)}={f(3), f(1)+f(2)}={1, 1},G(5)={0, 2, 3, …},f(5)=0;
g(6)={#(4), #(1, 3), #(2, 2)}={2, 1, 0},G(6)={3, 4, …},f(6)=3;
g(7)={#(5), #(1, 4), #(2, 3)}={0, 2},G(7)={1, 3, 4, …},f(7)=1;
图2所示的局面S=(7, 3, 3),有#S=f(7)+f(3)+f(3)=1+1+1=1,故S胜。
游戏C的初始局面S=(3, 4, 6),有#S=1+2+3=01+10+11=0,故S负。
此类搏弈游戏的一般性解法:
用一个n元组(a1, a2, …, an),来描述游戏过程中的一个局面。
用符号#S,表示局面S所对应的二进制数。
用符号$(x),表示局面(x)的下一步所有可能出现的局面的集合。
定义集合g(x):设$(x)={S1, S2, …, Sk},则g(x)={#S1, #S2, …, #Sk}。
令非负整数集为全集,集合G(x)表示集合g(x)的补集。
定义函数f(n):f(n)=min{G(n)},即f(n)等于集合G(n)中的最小数。
设局面S=(a1, a2, …, an),#S=f(a1)+f(a2)+…+f(an),采用二进制数的加法。
若#S≠0,则先行者有必胜策略;若#S=0,则后行者有必胜策略。
实现的一个代码如下:
int cmp(const void *a,const void *b)
{
return (int )a-(int )b;
}
int f[1000]={0,0,1,1};//初始化前4个
int g[500],c[1000];
int main()
{
int i,j,k,m,n,s;
for(i=4;i<=1000;i++)
{
m=0;
for(j=1;j<=i/2;j++)
{
g[m++]=f[j-1]^f[i-j-1];
}
qsort(g,m,sizeof(g[0]),cmp);//排序
for(k=0;;k++)
if(g[k]!=k)//找其补集的最小值
{
f[i]=k;//找到f[i]
break;
}
//printf(“a[%d]=%d\n”,i,a[i]);
}
while(scanf(“%d”,&n)!=EOF&&n)
{
for(i=0;i
公平游戏的Sprague-Grundy定理
公平游戏是一种双人游戏,在游戏中双方都有完整的信息,没有牵涉,任何状态的合法操作对双方来说都是相同的。
一个公平游戏可以抽象地用一个有向无环图来表示,这个图中每个点都对应这一个状态,每条有向边代表从一个状态到另一个状态的合法操作。
我们可以想象一个代币最初放在某个点上,然后两个玩家轮流将其从当前的点移动到它的后继点。当代币移动到汇点时游戏结束,汇点是一个没有出度的点,最后一个需要操作的玩家就是胜者。
P- 和 N-状态
如果双方都按照最佳策略进行游戏,我们可以将游戏中的每个状态依据其是先手必胜还是后手必胜分类。
一个先手胜状态被认为是一个N-状态(因为下一个玩家即将获胜),一个后手胜状态被认为是一个P-状态(因为前一个玩家即将获胜)
P-和N-状态归纳性地描述如下:
一个点v是P-状态当且仅当它的所有后继都为N-状态
一个点v是N-状态当且仅当它的一些后继是P-状态
这个归纳从汇点开始,汇点是P-状态因为它显然满足P-状态的要求。
游戏的P-和N-状态的信息提供了它的必胜策略。如果轮到我们且游戏处在一个N-状态,我们应该转移到一个P-状态。接着我们的对手就会被迫进入N-状态,依此类推。我们最终会移入一个汇点并获得胜利。
游戏的和
如果G1和G2 是公平游戏,那么他们的和G1 + G2是另一个公平游戏,玩法如下:每个回合,一个玩家选择G1, G2 中的一个(随便哪个他希望的)然后玩它,不碰另一个游戏。当 G1 和 G2都不能操作时游戏结束。
形式上,如果 G1 = (V1, E1) 和 G2 = (V2, E2)是游戏图,那么他们的和 Gsum = (Vsum, Esum) 规定为:
Vsum = V1 × V2,
Esum = {(v1v2, w1v2) | (v1, w1) ∈ E1} ∪ {(v1v2, v1w2) | (v2, w2) ∈ E2}.
现在,假定我们给出两个游戏G1 和 G2。如果我们只知道单个游戏的P-状态和N-状态我们能够正确地玩好游戏和G1 + G2吗?答案是否定的。不难看出两个P-状态的和总是P-状态,P-状态和N-状态的和总是N-状态。但是两个N-状态的和既可能是P-状态也可能是N-状态。因此,只知道单个游戏的P-状态和N-状态是不够的。
为了正确地玩好游戏和我们需要推广P-状态和N-状态,它就是Sprague-Grudy函数(或者简称为Grundy函数)。
The Sprague-Grundy function
Sprague-Grundy 函数
令N = {0, 1, 2, 3, …} 为自然数的集合。Sprague-Grundy 函数给游戏中的每个状态分配了一个自然数。结点v的Grundy值等于没有在v的后继的Grundy值中出现的最小自然数。
形式上:给定一个有限子集 S ⊂ N,令mex S(最小排斥值)为没有出现在S中的最小自然数
mex S = min (N S).
现在,给定一个游戏图G=(V,E),其Sprague-Grundy函数g:V → N 归纳定义为
g(v) = mex {g(w) | (v, w) ∈ E}.
从G的汇点开始归纳,可知它的Grundy值为0
Sprague-Grundy函数满足两个重要性质:
点v是一个P-状态当且仅当g(v)=0
如果G = G1 + G2 且 v = v1v2 是G的一个状态,那么g(v) 为g(v1) 和 g(v2) 在二进制下的异或:
g(v) = g(v1) ⊕ g(v2).
运算⊕也称作nim和。举个例子,3 ⊕ 5 = 011 ⊕ 101 = 110 = 6。类似地,3 ⊕ 6 = 5 且 5 ⊕ 6 = 3。
不难利用归纳法证明上面两个性质。
根据这些性质有v = v1v2 是P-状态当且仅当g(v1) = g(v2), 因为这是唯一能够使得nim和为0的途径。
无疑,游戏的求和是满足交换律和结合律的运算,nim和运算也是。
因此,我们可以通过获知单个游戏的Grundy函数来正确地玩好任意数目游戏和。
我们的策略如下:如果轮到我们且游戏的Grundy值给出了一个非0的nim和,那么必然在游戏的某个组分中存在一个操作使得nim和变为0。我们应该执行这个操作,那么接着我们的对手就被迫再次使得nim和非0。最终,我们将成为在最后一个游戏执行最后一个操作的人,最后将nim和变为0.
The game of Nim
Nim游戏
最基本的公平游戏是Nim堆。一个Nim堆由确定数目代币组成。在每个回合,一个玩家从堆上拿走1到整堆中任意数目的代币。拿空整堆的人获得胜利。
这个游戏如果独立看是没有意义的:先手玩家可直接拿走所有代币并立即获得胜利!
但是如果我们将各种大小的Nim堆加在一起,我们就得到了著名的Nim游戏。
大小为n的Nim堆的Grundy值为n。因此,Nim游戏中每个状态的Grundy值为每堆大小的Nim和。
Games that decompose into sums of themselves
一些分解成自身和的游戏
Sprague-Grundy定理最自然的应用就是一些分解成自身和的一些游戏。
考虑下面这个游戏:有一个大小为m*n的棋盘,且有无限数目某特定形状的骨牌供应。在每个回合,玩家在棋盘上一个空位放置一个骨牌,不能放骨牌的玩家就是败者。
在游戏期间,棋盘会逐渐分成不同的区域,对其我们可以分别计算Grundy值。
再举个例子,考虑Grundy游戏。这个游戏的一个状态由一些不同大小的代币堆组成,一次操作由只取一堆并把它分成两个不相等的堆组成。当所有堆的大小只有1和2的时候游戏结束,因为它不能再分。
令g(n)为单个大小为n的堆的Grundy值。数列g(n)如下:
n: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20…
g(n):0 0 1 0 2 1 0 2 1 0 2 1 3 2 1 3 2 4 3 0
比如:
当n等于1,2时已满足条件,即不能再取,也就没有下一个局面,所以g(1)={};所以G(1)={0,1,2,3,4…};
所以g(1)=0;同理g(2)=0;依次递推,g(3),g(4),g(5)等,
例如:g(6)={#(1,5),#(2,4)}={g(1)+g(5),g(2)+g(4)}=g(2,0);
所以G(6)={1,3,4,5,6…},所以g(6)=1;
甲乙两人面对若干排石子,其中每一排石子的数目可以任意确定。例如图所示的初始局面:共n=3排,其中第一排的石子数a1=7,第二排石子数a2=3,第三排石子数a3=3。两人轮流按下列规则取走一些石子,游戏的规则如下:每一步必须从某一排中取走两枚石子;这两枚石子必须是紧紧挨着的;如果谁无法按规则取子,谁就是输家。
解:
用符号#S,表示局面S所对应的二进制数。
用符号$(x),表示局面(x)的下一步所有可能出现的局面的集合。
定义集合g(x):设$(x)={S1, S2, …, Sk},则g(x)={#S1, #S2, …, #Sk}。
函数f满足要求的一个充分条件
f(a1)不属于集合g(a1)。
集合g(a1)包含集合{0, 1, …, f(a1)–1}。
如果g(a1)={0, 1, 2, 5, 7, 8, 9},则f(a1)=3,满足要求。
用大写字母N表示非负整数集,即N={0, 1, 2, …}。
令N为全集,集合G(x)表示集合g(x)的补集。
定义函数f(n):f(n)=min{G(n)},即f(n)等于集合G(n)中的最小数。
设局面S=(a1, a2, …, an),#S=f(a1)+f(a2)+…+f(an),采用二进制数的加法。
若#S=0,则S负;若#S≠0,则S胜。
游戏C的f值:
g(0)={},G(0)={0, 1, …},f(0)=0;
g(1)={},G(1)={0, 1, …},f(1)=0;
g(2)={#(0)}={f(0)}={0},G(2)={1, 2, …},f(2)=1;
g(3)={#(1)}={f(1)}={0},G(2)={1, 2, …},f(3)=1;
g(4)={#(2), #(1, 1)}={f(2), f(1)+f(1)}={1, 0},G(4)={2, 3, …},f(4)=2;
g(5)={#(3), #(1, 2)}={f(3), f(1)+f(2)}={1, 1},G(5)={0, 2, 3, …},f(5)=0;
g(6)={#(4), #(1, 3), #(2, 2)}={2, 1, 0},G(6)={3, 4, …},f(6)=3;
g(7)={#(5), #(1, 4), #(2, 3)}={0, 2},G(7)={1, 3, 4, …},f(7)=1;
图2所示的局面S=(7, 3, 3),有#S=f(7)+f(3)+f(3)=1+1+1=1,故S胜。
游戏C的初始局面S=(3, 4, 6),有#S=1+2+3=01+10+11=0,故S负。
此类搏弈游戏的一般性解法:
用一个n元组(a1, a2, …, an),来描述游戏过程中的一个局面。
用符号#S,表示局面S所对应的二进制数。
用符号$(x),表示局面(x)的下一步所有可能出现的局面的集合。
定义集合g(x):设$(x)={S1, S2, …, Sk},则g(x)={#S1, #S2, …, #Sk}。
令非负整数集为全集,集合G(x)表示集合g(x)的补集。
定义函数f(n):f(n)=min{G(n)},即f(n)等于集合G(n)中的最小数。
设局面S=(a1, a2, …, an),#S=f(a1)+f(a2)+…+f(an),采用二进制数的加法。
若#S≠0,则先行者有必胜策略;若#S=0,则后行者有必胜策略。
实现的一个代码如下:
int cmp(const void *a,const void *b)
{
return (int )a-(int )b;
}
int f[1000]={0,0,1,1};//初始化前4个
int g[500],c[1000];
int main()
{
int i,j,k,m,n,s;
for(i=4;i<=1000;i++)
{
m=0;
for(j=1;j<=i/2;j++)
{
g[m++]=f[j-1]^f[i-j-1];
}
qsort(g,m,sizeof(g[0]),cmp);//排序
for(k=0;;k++)
if(g[k]!=k)//找其补集的最小值
{
f[i]=k;//找到f[i]
break;
}
//printf(“a[%d]=%d\n”,i,a[i]);
}
while(scanf(“%d”,&n)!=EOF&&n)
{
for(i=0;i
相关文章推荐
- 华为OJ(统计每个月兔子的总数)
- UVA 10158 War 并查集
- Python并发编程
- maven使用Nexus私有仓库进行管理
- js方法格式化数字,加入千分符,并可以指定小数点位数
- IOS 多线程(4) --线程通讯
- HDU 2222 AC自动机
- 【POJ1456】【并查集变形】【技巧】
- 《数据库系统概念第六版》读书笔记
- C++常用术语及其英文翻译的含义和简单用途总结(八)
- accordion 全部折叠
- ARC(Automatic Reference Counting)
- 得到文件某行的偏移量(get byte offset of a line)
- mybatis 动态sql
- sql 查询所有数据库、表名、表字段总结
- 基于JQuery实现滚动到页面底端时自动加载更多信息
- vector删,erase和remove难怪--【STL】
- Android应用程序资源(一)
- 如果让我重新设计一款Android App
- Sql 位数不足补充 0或其他字符