爱思考的你,想没想过SG函数为什么偏偏要取异或和

SuperJvRuo

2018-02-19 20:37:28

Personal

## 零、一些碎碎念 作为OIer,我们总会遇到一些博弈论题目。 [【P1247】取火柴游戏](https://www.luogu.org/problemnew/show/P1247) [【P2252】取石子游戏](https://www.luogu.org/problemnew/show/P2252) [【P2148】【SDOI2009】E&D](https://www.luogu.org/problemnew/show/P2148) 曾经,学哥学姐或是教练抛出一个SG函数后告诉我们: 对于这样的题目,将所有的mex(minimum excluded)取xor和,如果结果为0,我们面对的就是败态;否则就是胜态。 一对聪明绝顶的CP:Alice和Bob, ![](https://cdn.luogu.com.cn/upload/pic/14657.png ) 一套使人一头雾水的博弈论, ![](https://cdn.luogu.com.cn/upload/pic/14656.png ) 一个神来之笔般的SG函数, ![](https://cdn.luogu.com.cn/upload/pic/14655.png ) 一个让人匪夷所思的xor运算…… 想必所有人学到博弈论,用SG函数AC掉一道又一道题时,都会~~一边感叹着这函数真TM好用,一边~~在脑中冒出一个大大的问号:这么牛逼的SG函数以及异或运算都是怎么想出来的呢? 这篇博客,就来谈一谈SG函数的发现与证明。 ## 一、Nim博弈 初始时有若干堆石子,游戏双方轮流选定一堆并从中拿走任意颗石子(可以把一堆拿光),拿走最后一颗石子者为胜。 ![]( https://cdn.luogu.com.cn/upload/pic/14605.png ) 显然,这道[【P1247】取火柴游戏](https://www.luogu.org/problemnew/show/P1247)就是Nim博弈的板子题。 ## 二、策梅洛定理 自然地,我们会关心一个问题:给定一个初始状态,先手玩家是否有必胜策略? 其实啊,我们如果倒着来推的话: 对于一个终局状态: 根据规则,我们就可以钦定面对此状态的人的胜负,在Nim博弈中,终局状态(即所有石子都被对面取光)是负态,我们可以把终态视为一种特殊的负态; 对于一个非终局状态: **如果我们能留给对手的所有可能状态都是胜态,那么该状态为负态;** **如果我们能留给对手的所有可能状态中存在负态,那么该状态为胜态。** 所以,此游戏中的任何一个可达到的状态,要么是胜态,要么是负态,前提是: * 双人、回合制; * 信息完全公开; * 无随机因素; * 必然在有限步内结束; * 没有平局。 这就是策梅洛定理(Zermelo's theorem)。 由此亦可知,**经过一步行动,负态只能变成胜态,胜态可以(但不一定)变成负态。** 同时,根据这样的性质,我们就可以利用记忆化搜索来暴力解决某些博弈论问题,比如这道:[【Trie、博弈论】CF455B A Lot of Games](https://www.luogu.org/problemnew/show/CF455B),或是打表找出规律。 ## 三、状态的组合 ### 1、状态组合的性质 通常来说,在题目中的游戏里,玩家的任意一步行动都只能影响一个子状态(对于E&D这样的题来说,我们将一个二元组视为一个子状态)。 显然,Nim博弈就是一些子状态(一堆石子/火柴)的组合。如果能利用子状态的胜负情况推出状态组合的胜负情况,我们就可以看出Nim博弈的胜负了。 我们不妨定义状态组合运算@(这是我随便定义的),状态组合运算@应具有以下性质: * **交换律**:如果有两堆石子构成二元组$ (a,b) $,显然,状态$ (b,a) $和它是一样的,即$ a @ b == b @ a $; * **结合律**:如果有三堆石子$(a,b,c)$,显然,a与$(b,c)$组合和$(a,b)$与c组合也是一样的,即$a @ (b @ c) == (a @ b) @ c$。 因为@具有结合律,所以我们只要研究明白两个状态如何组合,就可以进行若干个状态的组合了。 ### 2、两个状态的组合结果 #### (1)、负态 @ 负态 负态 @ 负态 = 负态。先手只能将组合变成胜态 @ 负态;而后后手就可以把胜态变成负态,把负态 @ 负态返还给先手方。直到两个负态均为终态为止。 #### (2)、胜态 @ 负态 由(1)可知,这种状态是胜态。必胜策略就是把胜态变成负态,把负态 @ 负态留给后手。 #### (3)、胜态 @ 胜态 对于这种情况,我们不应把一个胜态转化为负态,因为你将把胜态 @ 负态 = 胜态留给后手。所以我们不得不把胜态转化成另一个胜态,把胜态 @ 胜态留给后手。后手自然会采取与你相同的策略。 然而,游戏是有限的,迟早有一个玩家仅能把局面转化成胜态 @ 负态,输掉游戏。问题是,我们不知道会是哪个玩家、在哪一步输掉游戏。因此,我们需要知道两个胜态的更多性质。 ## 四、SG函数 举个例子: 有四堆石子:1、2、3、7。分开单独来看,每堆石子也就是每个子状态,都是胜态。 第一堆石子1只能变成终负态0。我们不妨称它为一级胜态。第二堆石子2只能变成1或0,可以被称为二级胜态。以此类推,3是三级胜态,7是七级胜态。 我们就发明了如下定义:**一个胜态可以变为1~n-1级的胜态,则称该胜态为n级胜态。**这个级数也就是对该胜态的所有后续情况取**mex(minimum excluded,不包含在内的最小自然数)**的结果。 胜态组合的胜负,与胜态的级数密切相关。 当你面对两个级数相同的胜态时,你只能认输,因为你的每一次操作都可以被对手拷贝。你取走这一堆的一颗石子,他就会取走另一堆的一颗石子。于是,当你取光一堆石子时,另一堆也被对手取光,对手就赢得了胜利。 而当你面对两个级数不同的胜态时,你就可以把级数较高的胜态转化为另一个胜态的同级胜态,把两个级数相同的胜态留给对手,你便获胜了。 其实啊,我们定义的胜态级数正是传说中的SG函数: $$ SG(A)=mex \lbrace SG(B)|A\rightarrow B \rbrace $$ ## 五、SG函数的组合 ### 1、SG函数组合的猜想 我们继续第三节的思考,现在我们解决了两个胜态组合后的胜负情况,但我们还要求出这两个组合成的状态的SG函数值,才能进行更多状态间的组合运算。 我们已经知道状态组合$@$有如下性质: * 交换律:如果有两堆石子构成二元组$ (a,b) $,显然,状态$ (b,a) $和它是一样的,即$ a @ b == b @ a $; * 结合律:如果有三堆石子$(a,b,c)$,显然,a与$(b,c)$组合和$(a,b)$与c组合也是一样的,即$a @ (b @ c) == (a @ b) @ c$。 * 状态$A@0=A$,一堆空的石子对结果没有影响 * 状态$A@A=0$,两个SG相同的状态组合是负态 当两个状态的SG函数不同且都不为0时呢?我们举几个例子: 1. $(1,2)$的后续情况包括:$SG(0@2)=2$,$SG(1@0)=1$,$SG(1@1)=0$。故$SG(1@2)=mex(\lbrace SG(1@A)|2\rightarrow A\rbrace\bigcup \lbrace SG(B@2)|1\rightarrow B\rbrace)=3$ 2. $(1@3)$的后续情况包括:$SG(0@3)=3$,$SG(1@0)=1$,$SG(1@1)=0$,$SG(1@2)=3$。故$SG(1,3)=mex(\lbrace SG(1,A)|3\rightarrow A\rbrace\bigcup \lbrace SG(B,3)|1\rightarrow B\rbrace)=2$ 3. $(2,3)$的后续情况包括:$SG(0,3)=3$,$SG(1,3)=2$,$SG(2,0)=2$,$SG(2,1)=3$,$SG(2,2)=0$。故$SG(2,3)=mex(\lbrace SG(2,A)|3\rightarrow A\rbrace\bigcup \lbrace SG(B,3)|2\rightarrow B\rbrace)=1$ 当然,我们是学过的,我们知道$SG(A@B)=SG(A) xor SG(B)$,也的确,$Axor0=A$,$AxorA=0$,$1xor2=3$,$1xor3=2$,$2xor3=1$。 但是, ![](https://cdn.luogu.com.cn/upload/pic/14653.png ) 我们只是通过举例的方法,发现了状态的组合对应着SG函数的异或。我们并没有证明通过子状态的SG函数能够唯一确定母状态的SG函数(即$xor$运算结果的唯一性),也没有证$xor$是能够达到这个目的的唯一一种运算。 能否证明出xor的正确性呢? ### 2、xor运算正确性的证明 已知:上面我胡扯的所有内容 求证:$SG(A@B)=SG(A) xor SG(B)$,其中$@$代表状态的组合 证明: 由SG函数定义可知$SG(A@B)=mex(\lbrace SG(A@C)|B\rightarrow C\rbrace\bigcup \lbrace SG(D@B)|A\rightarrow D\rbrace)$ 我们可以按游戏状态的拓扑序排序,使用数学归纳法,终态$SG(0@0)=0=0xor0$成立,先使得$SG(A@B)=mex(\lbrace SG(A)xorSG(C)|B\rightarrow C\rbrace\bigcup \lbrace SG(D)xorSG(B)|A\rightarrow D\rbrace)$ 我们只需证明 1. $SG(A)xorSG(B)\notin (\lbrace SG(A)xorSG(C)|B\rightarrow C\rbrace\bigcup \lbrace SG(D)xorSG(B)|A\rightarrow D\rbrace)$ 2. $\forall E<SG(A)xorSG(B)$,有$E\in(\lbrace SG(A)xorSG(C)|B\rightarrow C\rbrace\bigcup \lbrace SG(D)xorSG(B)|A\rightarrow D\rbrace)$ 首先,证明第一个式子: $\because A\rightarrow C,B\rightarrow D$ $\therefore SG(A)\neq SG(C),SG(B)\neq SG(D)$ $\therefore SG(A)xorSG(B)\notin (\lbrace SG(A)xorSG(C)|B\rightarrow C\rbrace\bigcup \lbrace SG(D)xorSG(B)|A\rightarrow D\rbrace)$ 第二个式子的证明有些难了: 我们定义$F=SG(A)xorSG(B)xorE$。 用$F$与$SG(A),SG(B),E$三者异或,至少使得其中一者减小(因为$F$非零,$F$的二进制表示中最高位的$1$必来自$SG(A),SG(B),E$三者之一,$F$与这一者异或会使它减小)。 但这一者不会是$E$,因为$FxorE =SG(A)xorSG(B)> E$。 不妨设这一者是$SG(A)$,即有$FxorSG(A)=E xorSG(B)<SG(A)$。此时,可取A的一个次态C使得$SG(C) = ExorSG(B)$,由SG数的定义,这是一定能做到的。 这就证明了$\forall E<SG(A)xorSG(B)$,有$E\in(\lbrace SG(A)xorSG(C)|B\rightarrow C\rbrace\bigcup \lbrace SG(D)xorSG(B)|A\rightarrow D\rbrace)$。 故$SG(A+B) = SG(A) xor SG(B)$。 ## 六、上代码 ### P1247取火柴游戏 ``` #include<cstdio> #include<cctype> int k,n[500005]; int Read() { int x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); } return x; } int main() { k=Read(); int x=0; for(int i=1;i<=k;++i) { n[i]=Read(); x^=n[i]; } if(!x) { puts("lose"); return 0; } for(int i=1;i<=k;++i) { if((n[i]^x)>=n[i]) continue; printf("%d %d\n",n[i]-(n[i]^x),i); n[i]^=x; break; } for(int i=1;i<=k;++i) printf("%d ",n[i]); } ```