博弈论与SG函数(Nim游戏)
在本篇,我们考虑的是两个玩家进行游戏,并且不存在随机概率因素(例如随机抽卡)。
我们的目标是找到一个最优策略去赢得游戏而不关心谁是对手。
游戏状态
让我们考虑一个游戏,这有一堆棍子,两个游戏者A和B依次从这一堆棍子中抽走一些,每次只能抽走1,2或3个棍子,最终谁抽走了最后一根棍子谁就是赢家。
这个游戏包含 n + 1 n+1 n+1个状态,分别是 0 , 1 , 2 , … , n 0,1,2,\ldots,n 0,1,2,…,n,每一个状态 i i i代表着场上现在还剩下 i i i个棍子,而不管现在的游戏者是谁。
每一个游戏状态分类为两种,一种是赢态,一种是输态。博弈论定理告诉我们,如果一种游戏状态是赢态,那么它经过最优选择之后必然会转移到输态,否则这个游戏状态是输态。
例如 n = 15 n=15 n=15,状态 0 0 0必然是输态,因为已经没有棍子可以抓取,状态 1 , 2 , 3 1,2,3 1,2,3都是赢态,因为都有机会转移到状态 0 0 0,而状态 4 4 4是输态,因为这个状态没有办法转移到输态上。,依次类推,我们得到一个表格:
状态 | 赢/输 |
---|---|
0 | L |
1 | W |
2 | W |
3 | W |
4 | L |
5 | W |
6 | W |
7 | W |
8 | L |
9 | W |
10 | W |
11 | W |
12 | L |
13 | W |
14 | W |
15 | W |
做出这个表之后,就很容易的分析这个游戏,我们发现是 4 4 4的倍数都是输态,开局 15 15 15是赢态,这说明先手必然会赢。
如果 n = 16 n=16 n=16,那么开局 16 16 16这个状态是输态,那么先手必然会输。
以上的必然性都是建立在双方都是以最优策略进行博弈,否则不一定存在必然性。
状态图(SG图)
状态图(State Graph)是一个DAG图,揭示了游戏状态如何进行转移,此类游戏必须满足下列条件,才能使用SG图分析:
- 游戏有两个人参与,二者轮流做出决策,双方均知道游戏的完整信息;(保证状态转移是唯一的)
- 任意一个游戏者在某一确定状态可以作出的决策集合只与当前的状态有关,而与游戏者无关;(与游戏者无关)
- 游戏中的同一个状态不可能多次抵达,游戏以玩家无法行动为结束,且游戏一定会在有限步后以非平局结束。(保证是有限DAG图)
满足这些条件的游戏也叫做公平组合游戏。
我们再考虑一个更复杂的游戏:
每次我们拿取的棍子的数量必须是 i i i的倍数,并且必须小于 i i i,比如说状态 8 8 8,游戏者只能拿 1 , 2 , 4 1,2,4 1,2,4个棍子。
当 n = 9 n=9 n=9时,我们可以做出状态转移图(SG图):
我们可以确定 1 1 1总是输态,因为游戏已经终止,无法操作。
同样,我们可以做出状态表:
状态 | 赢/输 |
---|---|
1 | L |
2 | W |
3 | L |
4 | W |
5 | L |
6 | W |
7 | L |
8 | W |
9 | L |
我们发现奇数一定是输态,偶数一定是赢态。
Nim 游戏
有 n n n 堆物品,每堆有 a i a_i ai 个,两个玩家轮流取走任意一堆的任意个物品,但不能不取,取走最后一个物品的人获胜。
Nim 游戏的状态是一个 n n n元组,定义为 [ x 1 , x 2 , … , x n ] [x_1,x_2,\ldots,x_n] [x1,x2,…,xn],其中 x i x_i xi为第 i i i堆的物品的数量。
更一般的,我们总结如下定理:
- 定理 1:没有后继状态的状态是必败状态。
- 定理 2:一个状态是必胜状态当且仅当存在至少一个必败状态为它的后继状态。
- 定理 3:一个状态是必败状态当且仅当它的所有后继状态均为必胜状态。
对于定理 1,如果游戏进行不下去了,那么这个玩家就输掉了游戏。
对于定理 2,如果该状态至少有一个后继状态为必败状态,那么玩家可以通过操作到该必败状态;此时对手的状态为必败状态——对手必定是失败的,而相反地,自己就获得了胜利。
对于定理 3,如果不存在一个后继状态为必败状态,那么无论如何,玩家只能操作到必胜状态;此时对手的状态为必胜状态——对手必定是胜利的,自己就输掉了游戏。
有了以上三条定理和SG图,我们可以在 O ( ∏ a i ) O(\prod a_i) O(∏ai)的时间内计算出所有的状态的输赢。
但是这样的时间复杂度太高,我们有更好的办法去解决这个问题。
Nim 和
定义一个状态的Nim和为 S = x 1 ⊕ x 2 ⊕ … ⊕ x n S=x_1 \oplus x_2 \oplus \ldots \oplus x_n S=x1⊕x2⊕…⊕xn。
一个状态为输态,当且仅当 S = 0 S=0 S=0,否则为赢态。
为什么状态与异或和有关?
我们需要证明以下三个定理:
- 定理 1:没有后继状态的状态是必败状态。
- 定理 2:对于 S ≠ 0 S \neq 0 S=0 的局面,一定存在某种移动使得 S = 0 S = 0 S=0。
- 定理 3:对于 S = 0 S = 0 S=0 的局面,一定不存在某种移动使得 S = 0 S = 0 S=0。
对于定理1,即结束 [ 0 , 0 , … , 0 ] [0,0,\ldots,0] [0,0,…,0]的异或和一定为 0 0 0。
对于定理2,我们会修改 x i x_i xi,使得变为 x i ′ x_i' xi′,且 x i ′ < x i x_i' < x_i xi′<xi, x i ′ = k ⊕ x i x_i' = k \oplus x_i xi′=k⊕xi。记 S S S在二进制下的最高有效位为 b b b,我们总是能找到一个 x i x_i xi在二进制下的 b b b位为 1 1 1,那么我们就可以将这一位变为 0 0 0,进而可以调整后面的位,使得 S = 0 S=0 S=0,并且调整过后的 x i ′ < x i x_i' < x_i xi′<xi。
对于定理3,修改 x i x_i xi的任意一位都会使 S S S变化,因此一定不存在某种移动使得 S = 0 S = 0 S=0。
SG函数
Sprague-Grundy 定理总结了一般的组合博弈游戏的解决方案。
- 这有两个玩家互相进行游戏。(回合制)
- 游戏的局面由状态组成,每一个状态的移动都是确定的,不依赖于当前状态是谁的回合。(玩家无关性)
- 当玩家无法进行操作的时候游戏便结束。因此,这个游戏迟早会结束。(有穷性)
- 在游戏开始之前,游戏玩家就已经知道所有状态的转移路径,并且无法改变,也不存在概率随机因素。(确定性)
SG函数的思想就像定义Nim和一样,我们为每一个状态定义一个Grundy数字。
Grundy数字
定义一个状态的Grundy数字(Grundy-Number)为:
G N k = mex ( { g 1 , g 2 , … , g n } ) GN_k = \text{mex}(\{g_1,g_2,\ldots,g_n\}) GNk=mex({g1,g2,…,gn})
这里的 g 1 , g 2 , … , g n g_1,g_2,\ldots,g_n g1,g2,…,gn是我们量化一个状态的数字, g 1 , g 2 , … , g n g_1,g_2,\ldots,g_n g1,g2,…,gn分别是状态 k k k的后继状态的 G N GN GN。
而 mex \text{mex} mex函数的意思是,返回没有出现在 g 1 , g 2 , … , g n g_1,g_2,\ldots,g_n g1,g2,…,gn最小的非负整数,例如: mex ( { 0 , 1 , 3 } ) = 2 \text{mex}(\{0,1,3\})=2 mex({0,1,3})=2。
我们定义 S G SG SG函数为:
S G ( k ) = G N k = mex y i ∈ Y ( { S G ( y i ) } ) SG(k) = GN_k = \text{mex}_{y_i \in Y}(\{SG(y_i)\}) SG(k)=GNk=mexyi∈Y({SG(yi)})
其中 Y Y Y集合是状态 k k k的后继状态的集合。
特别的,终止状态没有后继,我们记 S G ( ϕ ) = 0 SG(\phi) = 0 SG(ϕ)=0。
这样,每一个有向图都可以转换成一个SG图,节点上的点权为该状态的SG函数的值。
例如:
并且有第一SG定理:
一个状态是输态当且仅当 G N = 0 GN = 0 GN=0,否则为赢态(SG函数值为正整数)。
当一个状态的 G N = 0 GN=0 GN=0那么其后继节点一定都是正整数,如果一个状态是 x x x为正整数,那么,他的前驱一定包括 0 , 1 , … , x − 1 0,1,\ldots,x-1 0,1,…,x−1,特别的一定包括 0 0 0。
正好满足我们SG图的定义。
组合博弈游戏
考虑如下一个游戏,两个玩家面前有一个棋盘,有些地方是可以走的,有些地方是不能走的,每个回合,一个玩家移动人物,可以连续移动人物向上走,或者连续移动人物向右走,不能不移动。当移动不了人物的时候游戏结束,并且最后一个移动人物的玩家获胜。
其中"@“代表的当前人物的位置,”*"代表的是可以移动的范围,图二写出了每一个点的 G N GN GN值。
这是一个单个Nim 游戏,类似的我们可以构造出组合Nim游戏。玩家现在有 n n n个棋盘,每个回合玩家可以任选一个棋盘进行操作。
按照Nim 和的思想,我们可以定义这个游戏的Nim 和为:
S = a 1 ⊕ a 2 ⊕ … ⊕ a n S = a_1 \oplus a_2 \oplus \ldots \oplus a_n S=a1⊕a2⊕…⊕an
其中 a i a_i ai是第 i i i个棋盘的 S G SG SG函数值或 G N GN GN值。
同理,当且仅当该状态的 S = 0 S=0 S=0,这个状态是输态,否则是赢态。
如此,我们有第二SG定理:
一个组合Nim 游戏的初始 S S S值如果为 0 0 0,那么先手一定必输。否则先手必赢。
同样的,此定理在双方都是最优策略的情况下必然性成立。
Grundy 游戏
Grundy 游戏与组合博弈游戏不同的是,Grundy 游戏中存在单一Nim游戏,也存在组合Nim游戏。
例如,我们还是考虑木棍游戏。两个游戏玩家每个回合任选一堆棍子,然后把它们分成数量不相等的两堆棍子,最后无法分解游戏结束,最后一个分解的玩家胜利。
我们定义游戏状态为 f ( n ) f(n) f(n),其中 n n n为棍子的数量,且只有一堆棍子。例如 n = 8 n=8 n=8,那么计算 f ( 8 ) f(8) f(8), 8 8 8可以有如下拆分为 1 + 7 1+7 1+7, 2 + 6 2+6 2+6或 3 + 5 3+5 3+5,那么:
f ( 8 ) = mex ( { f ( 1 ) ⊕ f ( 7 ) , f ( 2 ) ⊕ f ( 6 ) , f ( 3 ) ⊕ f ( 5 ) } ) f(8) = \text{mex}(\{f(1) \oplus f(7),f(2) \oplus f(6),f(3) \oplus f(5)\}) f(8)=mex({f(1)⊕f(7),f(2)⊕f(6),f(3)⊕f(5)})
这个步骤可以通过动态规划来计算出全部的 f ( n ) f(n) f(n)的值。计算出 f ( 8 ) = 2 f(8) = 2 f(8)=2,因此当 n = 8 n=8 n=8的时候,先手必胜。
例题
#include <bits/stdc++.h>
using namespace std;
#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)
typedef long long ll;
void solve()
{
int n;
scanf("%d", &n);
int XOR = 0;
for (int i = 1; i <= n; i++)
{
int val;
scanf("%d", &val);
XOR ^= val;
}
if (XOR == 0)
{
printf("No\n");
}
else
{
printf("Yes\n");
}
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}