前言

原文章

通过数论或者自然数性质完美解决的三个常见博弈游戏:

博弈 解决方法
Bash Game 同余理论
Nim Game 异或理论
Wythoff Game 黄金分割

Bash Game

描述:
一堆 n 个物品,两人轮流取,每次取 1 至 m 个,最后取完者胜。

以先手为例,分析:
1. 面对 [1…m] 个局面,必胜。
2. 面对 m + 1 个局面,必败。
3. 如果可以使对手面临必败局面,那么必胜。
4. 如果不能使对手面临必败局面,那么必败。

进一步:
基础:1, 2,……, m 是必胜局面, m + 1是必败局面。
递推:m + 2,m + 3,……,2m + 1 是必胜局面,2m + 2是必败局面。

k(m + 1) 是必败局面,应该允许 k = 0,因为 0 显然也是必败局面。
在必败局和必胜局中,胜方的策略是:拿掉部分物品,使对方面临 k(m + 1) 的局面。

即:n % (m + 1) == 0 时,先手必败。

比如,有 10 个物品,每次只能取 1 到 5 个,则先手方必胜:先手方拿 4 个,对手无论拿多少个,先手方下次总能拿完。

从另一个角度思考该问题:如果物品数量随机,那么先手方胜利的概率是 m/(m + 1),后手方胜利的概率是 1/(m + 1)。

Nim Game

描述:
m 堆 n 个物品,两人轮流取,每次取某堆中不少于 1 个,最后取完者胜。

分析:
1. 所有物品数目二进制异或不为 0,则先手必胜。
2. 所有物品数目二进制异或为 0,则后手必胜。

从另一个角度思考该问题:如果物品数量随机,那么每个数目的每一位上 1 或 0 概率相同:
- 如果有奇数个堆,那么 1 的个数为偶数或者奇数的概率相同,
- 如果有偶数个堆,那么 1 的个数为偶数的概率略大 1/(m + 1),
也就是说异或结果的每一位为 0 或 1 的概率几乎差不多,而先手必败要求异或结果每一位都为 0,其实败的概率很小。

Wythoff Game

描述:
2 堆(ak, bk)(ak <= bk)物品,两人轮流取,每次从 1 堆中取 k 个或者从 2 堆中同时取 k 个,最后面对 (0, 0) 局面的败(设ak <= bk是为了忽略顺序的影响)。

以先手为例,分析:
1. 面对 (0,0) 局面必败。
2. 面对 (1,1)(2,2)…(n,n)、(0,1)(0,2)…(0,n) 局面必胜。
3. 如果可以使对手面临必败局面,那么必胜。
4. 如果不能使对手面临必败局面,那么必败。

进一步:
基础:(0,0) 是必败局面;(0,1)(0,2)…(0,n) 是必胜局面。
递推:

(1,2) 是必败局面;    (1,1)(1,3)(1,4)...(1,n)(2,2)(2,3)...(2,n) 是必胜局面,
(3,5) 是必败局面;    (3,3)(3,4)(3,6)(3,7)...(3,n)(5,5)(5,6)...(5,n) 是必胜局面,
(4,7) 是必败局面;    (4,4)(4,5)(4,6)(4,8)(4,8)(4,9)...(4,n)(7,7)(7,8)(7,9)...(7,n) 是必胜局面,
(6,10) 是必败局面;   (6,6)(6,7)(6,8)(6,9)(6,11)(6,12)(6,13)...(6,n)(10,10)(10,11)(10,12)...(10,n) 是必胜局面。

规律:(必败局面的规律比较容易找到)
ak 是前面必败局未出现的数中最小者,
bk = ak + k (k = 0,1,2,3,…n)

必败局(奇异局)的重要性质:1,2,…,n 中每一个自然数,出现且只出现在一个奇异局中。

推导:
1. 由于 ak 总是选择未出现的数,所以每个数总能出现在奇异局中且 ak 不会选择到重复的数。
2. bk = ak + k,所以 bk 总是比前面所有奇异局出现的数都大,所以 bk 不会选择到重复的数。

必胜方的策略是:始终让对手面对必败局(奇异局)。

给定任意局势 (a,b),判定 (a,b) 是否为必败局的方法:k = 0,1…n,黄金比例 φ = 1.618033,ak = [φ * k],bk = ak + k = [φ^2 * k]。
比如:
k = 0,ak = 0,bk = 0
k = 1,ak = 1,bk = 2
k = 2,ak = 3,bk = 5
k = 3,ak = 4,bk = 7

更好的一种判断策略:k = bk - ak,当 ak == [φ * k] 时,为奇异局。

从胜负概率角度,如果堆中数量随机,先手一方优势很大。

组合游戏 - SG 函数和 SG 定理

必胜点与必败点

必胜点和必败点的概念:
P 点(Previous player):必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败。
N 点(Next player):必胜点,处于此情况下,双方操作均正确的情况下必胜。

必胜点和必败点的性质:
1. 所有终结点是必败点 P。
2. 从任何必胜点 N 操作,至少有一种方式可以进入必败点 P。
3. 无论如何操作,必败点 P 都只能进入必胜点 N。

通常我们分析必胜点和必败点都是以终结点进行逆序分析。

例子

HDU 1847 Good Luck in CET-4 Everybody!

打牌的规则:
1. 总共 n 张牌;
2. 双方轮流抓牌;
3. 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…);
4. 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者。

假设 Kiki 和 Cici 都足够聪明,并且每次都是Kiki先抓牌,请问谁能赢呢?

分析:
当 n = 0 时,显然为必败点,因为此时你已经无法进行操作了。
当 n = 1 时,因为你一次就可以拿完所有牌,故此时为必胜点。
当 n = 2 时,也是一次就可以拿完,故此时为必胜点。
当 n = 3 时,要么剩 1 张要么剩 2 张,无论怎么取对方都将面对必胜点,故这一点为必败点。

以此类推:

0 1 2 3 4 5 6 ...
P N N P N N P ...

你发现了什么没有,对,他们就是成有规律,使用了 P/N 来分析,有没有觉得问题变简单了。

SG 函数和 SG 定理

组合游戏的和通常是很复杂的,但是有一种新工具,可以使组合问题变得简单 ———— SG 函数和 SG 定理。

Sprague-Grundy 定理:

游戏和的 SG 函数等于各个游戏 SG 函数的 Nim 和。这样就可以将每一个子游戏分而治之,从而简化了问题。而 Bouton 定理就是 Sprague-Grundy 定理在 Nim 游戏中的直接应用,因为单堆的 Nim 游戏 SG 函数满足 SG(x) = x。

Sprague-Grundy 函数:

首先定义 mex(minimal excludant) 运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。比如,mex{0, 1, 2, 4} = 3、mex{2, 3, 5} = 0、mex{} = 0。

对于任意状态 i,定义 SG(x) = mex(S),其中 S 是 i 后继状态的 SG 函数值的集合。如 i 有三个后继状态分别为 SG(a)、SG(b)、SG(c),那么 SG(i) = mex{SG(a), SG(b), SG(c)}。这样集合 S 的终态必然是空集,所以 SG 函数的终态为 SG(i) = 0,当且仅当 i 为必败点 P 时。

取石子问题

有 1 堆 n 个的石子,每次只能取{1, 3, 4}个石子,先取完石子者胜利,那么各个数的 SG 值为多少?

SG[0] = 0,f[] = {1, 3, 4}

i = 1,可以取走 1 - f{1} 个石子,剩余 {0} 个,SG[1] = mex{SG[0]} = mex{0} = 1;
i = 2,可以取走 2 - f{1} 个石子,剩余 {1} 个,SG[2] = mex{SG[1]} = mex{1} = 0;
i = 3,可以取走 3 - f{1, 3} 个石子,剩余 {2, 0} 个,SG[3] = mex{SG[2], SG[0]} = mex{0, 0} =1;
i = 4,可以取走 4 - f{1, 3, 4} 个石子,剩余 {3, 1, 0} 个,SG[4] = mex{SG[3], SG[1], SG[0]} = mex{1, 1, 0} = 2;
i = 5,可以取走 5 - f{1, 3, 4} 个石子,剩余 {4, 2, 1} 个,SG[5] = mex{SG[4], SG[2], SG[1]} = mex{2, 0, 1} = 3;

以此类推…..

i     0 1 2 3 4 5 6 7 8....
SG[i] 0 1 0 1 2 3 2 0 1....

由上例我们就可以得到 SG 函数值求解步骤:
1. 使用数组 f 将可改变当前状态的方式记录下来。
2. 使用另一个数组将当前状态 i 的后继状态标记。
3. 最后模拟 mex 运算,在标记值中搜索未被标记值的最小值,将其赋值给 SG(i)。
4. 不断重复 2~3 步,知道计算完 1~n 的函数值。

模版

// 可改变当前状态的方式,N为方式的种类
int[] f = new int[N];// 当前状态 i 的后继状态的集合
boolean[] isVisit = new boolean[MAXN];// 0~n的SG函数值
int[] SG = new int[MAXN];void getSG(int n) {// 因为SG[0]始终等于0,所以i从1开始for (int i = 1; i < n; i++) {// 每一次都要将上一状态的后继集合重置Arrays.fill(isVisit, false);for (int j = 0; j < N && f[j] <= i; j++) {// 将后继状态的SG函数值进行标记isVisit[SG[i - f[j]]] = true;}for (int j = 0;; j++) {// 查询当前后继状态SG值中最小的非零值if (!isVisit[j]) {SG[i] = j;break;}}}
}

实战

HDU 1848 Fibonacci again and again

任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的:
F(1)=1;
F(2)=2;
F(n)=F(n-1)+F(n-2)(n>=3);
所以,1,2,3,5,8,13……就是菲波那契数列。

在HDOJ上有不少相关的题目,比如1005 Fibonacci again就是曾经的浙江省赛题。

今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下:
1、这是一个二人游戏;
2、一共有3堆石子,数量分别是m, n, p个;
3、两人轮流走;
4、可以选择任意一堆石子,然后取走f个;
5、f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量);
6、最先取光所有石子的人为胜者;

假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。

Input
输入数据包含多个测试用例,每个测试用例占一行,包含3个整数m,n,p(1<=m,n,p<=1000)。
m=n=p=0则表示输入结束。

Output
如果先手的人能赢,请输出“Fibo”,否则请输出“Nacci”,每个实例的输出占一行。

Sample Input
1 1 1
1 4 1
0 0 0

Sample Output
Fibo
Nacci

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;public class Main {static int MAXN = 1001;static int N = 16;static int[] f = new int[N];static boolean[] isVisit = new boolean[MAXN];static int[] SG = new int[MAXN];public static void main(String[] args) throws IOException {StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));getFibonacci();getSG(MAXN);int m, n, p;while (true) {in.nextToken();m = (int) in.nval;in.nextToken();n = (int) in.nval;in.nextToken();p = (int) in.nval;if (m == 0 && n == 0 && p == 0) {break;}out.println((SG[n] ^ SG[m] ^ SG[p]) != 0 ? "Fibo" : "Nacci");}out.flush();}static void getFibonacci() {f[0] = f[1] = 1;for (int i = 2; i < N; i++) {f[i] = f[i - 1] + f[i - 2];}}static void getSG(int n) {for (int i = 1; i < n; i++) {Arrays.fill(isVisit, false);for (int j = 0; j < N && f[j] <= i; j++) {isVisit[SG[i - f[j]]] = true;}for (int j = 0;; j++) {if (!isVisit[j]) {SG[i] = j;break;}}}}
}

三个常见博弈游戏以及 SG 函数和 SG 定理相关推荐

  1. 组合博弈游戏 - SG函数和SG定理

    转载来自:http://blog.csdn.net/luomingjun12315/article/details/45555495 在介绍SG函数和SG定理之前我们先介绍介绍必胜点与必败点吧. 必胜 ...

  2. 组合游戏 - SG函数和SG定理

    在介绍SG函数和SG定理之前我们先介绍介绍必胜点与必败点吧. 必胜点和必败点的概念: P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败. N点:必胜点,处于此情况下,双方操作均正 ...

  3. (组合游戏)SG函数与SG定理详解

    文章目录 前言 什么是组合游戏 必胜点和必败点的概念 Sprague-Grundy(SG)定理 SG函数 前言   好久没写博客了,上一篇博客还是去年实训写的,一是因为寒假,二是因为随着难度的加深,学 ...

  4. (转载)--SG函数和SG定理【详解】

    在介绍SG函数和SG定理之前我们先介绍介绍必胜点与必败点吧. 必胜点和必败点的概念: P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败. N点:必胜点,处于此情况下,双方操作均正 ...

  5. SG函数和SG定理【详解】

    在介绍SG函数和SG定理之前我们先介绍介绍必胜点与必败点吧. 必胜点和必败点的概念: P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败. N点:必胜点,处于此情况下,双方操作均正 ...

  6. SG函数和SG定理(Sprague_Grundy)

    一.必胜点和必败点的概念 P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败.        N点:必胜点,处于此情况下,双方操作均正确的情况下必胜. 必胜点和必败点的性质:    ...

  7. SG函数和SG定理的运用

    SG函数和SG定理的运用 SG函数和SG定理常用于解决博弈论的相关问题.其中SG函数的求解主要根据MEX运算.什么是MEX运算?MEX( minimal excludant ) 字面上意思是最小除外的 ...

  8. 博弈论-SG函数和SG定理

    1.SG函数和SG定理是一个十分神奇的东西,有了它,绝大部分的博弈都可以被统一到这个上面,都可以使用SG函数解决.是一种解决博弈问题的十分方便的手段. 2.首先给出一些基本的定义:mex运算,这个是作 ...

  9. 再谈SG函数和SG定理

    今天考了一道博弈论的题,让我重新复习一下SG定理吧. 首先通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是"选择一堆石子并拿走若干颗(不能不拿)" ...

最新文章

  1. mysql 执行计划extra_MySQL执行计划extra中的using index 和 using where using index 的区别...
  2. SQLite与pandas
  3. mysql select a b_MySQL-mysql中,select a,b 与 select *效率到底差多少?
  4. python列表的用法
  5. 强迫症慎入:一大票让人看哭的音量键设计即将袭来
  6. netty冲突 play sbt_netty4 实现一个断点上传大文件功能
  7. 福建信息技术学院计算机系男生宿舍怎么样,广西职业技术学院宿舍怎么样
  8. C语言中,scanf与scanf_s的简单区别
  9. DataGridview动态添加列
  10. 果粉期待已久,iPhone 6s 或年底发布
  11. windows server 2008R2 部署WSUS 服务
  12. c语言设置一个选择数字的程序,C语言编一个数字益智小游戏
  13. PowerBuilder DataWindow 38个开发技巧
  14. maxpooling反向传播
  15. 一个通过网络转换Ico到Png图片的小小程序(Ico2Png)
  16. windows/ubuntn 快捷键和命令行使用
  17. 推荐3个生活工具类的小程序,非常实用哦!
  18. 在ThinkPad W500 A98上升级Windows 7以及安装硬件驱动和相关程序
  19. labspec6教程_LabSpec6软件功能–光谱扫描方式-Horiba.PDF
  20. 如何通过脚本开发Android

热门文章

  1. 基于微信小程序的移动智能学习平台的设计开发和实现
  2. 第一周,人工智能学习总结
  3. ADSL常见网络故障及排除
  4. vb.net程序可以在触摸屏上运行么_台达PLC VP-PM运动控制型在大型铣镗床上的应用...
  5. 未来五年数控切割机重点发展方向
  6. 基于AutoJs抖音极速版辅助App
  7. margin-left是什么意思
  8. Linux基础第二部分。done
  9. 游戏开发者的成长之路:C++经典项目控制台贪吃蛇(在GitHub热门项目上增添功能及修改bug)
  10. [转]一往无前 | 小米十周年,雷军公开演讲全文