组合游戏学习笔记 [补档]

基础

满足以下条件的游戏是组合游戏:

  • 有两人参与游戏, 轮流作出决策
  • 无法作出决策的人失败, 然后游戏结束
  • 游戏总能在有限次决策后结束
  • 游戏的同一个状态不会多次到达
  • 任意一个参与者在某一确定状态下可以作出的决策集合只与当前状态有关

定义先手必败状态为必败态, 先手必胜状态为必胜态, 则我们有

  • 无法进行任何移动的状态是必败态.
  • 可以移动到必败态的状态的是必胜态.
  • 所有移动都只能得到必胜态的状态是必败态.

考虑如何判断一个游戏中, 先手是否必胜: 根据定义, 由于游戏中任意状态都不可能重复出现, 因而每个状态都只能是必胜态或必败态, 并且可以根据上述规则计算出来, 进而判断游戏中先手是否必胜.

然而, 我们发现按照上述规则进行计算需要枚举每个状态以及它能转移到的所有状态, 复杂度开销较大, 因此考虑如何优化一些常见的组合游戏问题.

Bash博弈

Wythoff博弈

问题概述

两堆若干个物品, 两人轮流操作, 每次可以从一堆中取任意多个物品, 或者在两堆中取相同的任意多个物品, 不可以不取. 取走最后一个物品的人获胜. 问先手是否必胜.

解决

我们用二元组\((a, b)\)表示一种状态. 我们不妨假设\(a \le b\). 考虑任意两个必败态\((a_1, b_1)\)和\((a_2, b_2)\)之间的关系, 有如下性质:

  • \(a_1\), \(b_1\), \(a_2\), \(b_2\)之间两两不相等. 证明:
  • 当\(a = b\)时, 可以同时在两堆中取\(a\)个物品, 继而转移到结束状态. 因此\(a = b\)的状态为必胜状态.
  • 当我们只取一堆中的物品时, \(a\)或\(b\)中的一个数保持不变. 不妨假设\(a\)不变, 由于\((a, b)\)可以转移到\((a, c)\)(或者是\((c, a)\))的状态, 因而\((a, b)\)和\((a, c)\)不可能同时是必败状态, 因而任意两个必败状态中不可能出现重复的数字
  • \(a_1 - b_1 \ne a_2 - b_2\). 证明: 对于\(a_1 - b_1 = a_2 - b_2\)的两种状态, 我们对\((a_1, b_1)\)在两堆中同时取出\(a_1 - a_2\)个物品, 即可得到\((a_2, b_2)\)状态, 因而这两种状态不可能同时是必败状态.

猜测所有必败状态的形式: 我们将所有必败二元组按照首元排序, 显而易见, 第\(0\)组为\((0, 0)\), 其后的每个必败二元组中首元为未在之前出现过的最小正整数, 并且第\(n\)组中两个数之差为\(n\). 直观地, 这些必败状态的前几位是这样的:\((0, 0)\), \((1, 2)\), \((3, 5)\), \((4, 7)\), \((6, 10)\), \(\cdots\)

证明:

首先用数学归纳法证明这种方法得到的状态一定都是必败态.

第\(0\)组显然是必败状态. 假设我们已经得到了前\(n\)组必败状态, 考虑第\(n + 1\)组. 我们假设在第\(0\)到\(n\)组中未出现过的最小非负整数为\(m\), 则第\(n + 1\)组显然是\((m, m + n + 1)\). ,考虑如何证明这一状态是必败状态:

  • 假如在\(m\)中取物品, 得到\((m - k, m + n + 1)\). 由于\(m\)为前\(n\)组中未出现过的最小非负整数, 因而\(m - k\)一定已经出现过. 根据前面的性质, 由于任意两组必败状态中的数字都不相同, 因而\((m - k, m + n + 1)\)不是必败状态.
  • 假如在\(m + n + 1\)中取物品, 得到\((m, m + n + 1 - k)\), 则两堆物品之差为\(n + 1 - k\), 必定在之前出现过. 由于任意两组必败状态的两堆物品之差不相同, 因而\((m, m + n + 1 - k)\)不是必败状态.

因而\((m, m + n + 1)\)不能转移到必败状态, 因而我们按照这种方法得到的状态一定都是必败状态.

同时, 我们还需要证明, 所有必败状态都可以通过这种方法得到, 换而言之, 就是这种方法无法得到的状态都是必胜状态.

证明非常简单, 考虑一个状态\((a, b)\), 假如不能通过上述方法得到, 则必然存在另一个\((a, c)\)状态是必败状态. 由于\((a, b)\)可以转移到\((a, c)\), 因而\((a, b)\)是必胜状态, 证毕.

因而这种方法是正确的.

Nim博弈

问题概述

Nim博弈应该是组合游戏中最广为人知的一个了. 给定\(n\)石子, 每堆\(a_n\)个, 两个人轮流取, 每次可以从一堆中取走任意多个石子, 取走对后一颗石子的人获胜. 问先手是否必胜.

解决

直接放公式吧: 一个状态为必败状态, 当且仅当\(a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0\)

证明如下:

跟之前Wythoff博弈的证明一样, 首先还是用数学归纳法证明通过这个结论得到的状态都是必败态.

显而易见地, 对于\(a_1 = a_2 = \cdots = a_n = 0\), 有\(a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0\), 且是必败态.

考虑之后的所有\(a_1 \oplus a_2 \oplus \cdots \oplus a_n = 0\)的状态, 由于无论将哪一个\(a_k\)改为\(a_k‘\)都不能使得\(a_1 \oplus \cdots \oplus a_k‘ \oplus \cdots \oplus a_n = 0\), 因而这个状态不能转移至一个必败状态, 因而它本身为必败态.

然后证明不能通过这种方法得到的状态都是必胜态: 仍然是显而易见地, 我们可以通过将\(a_1 \oplus a_2 \oplus \cdots \oplus a_n \ne 0\)中的某一个数\(a_k\)改为\(a_k‘ < a_k\), 使得\(a_1 \oplus \cdots \oplus a_k‘ \oplus \cdots \oplus a_n = 0\), 因而它是必胜状态.

因而公式正确.

原文地址:https://www.cnblogs.com/ZeonfaiHo/p/8379692.html

时间: 2024-10-21 05:19:20

组合游戏学习笔记 [补档]的相关文章

[原博客] 组合游戏学习

阅读了<由感性认识到理性认识——透析一类搏弈游戏的解答过程>.<解析一类组合游戏>.<组合游戏略述——浅谈SG游戏的若干拓展及变形>这三篇论文,对组合游戏以及SG函数有了更深的理解.这篇文章摘下了这三篇论文的部分重要内容,以及部分我对组合游戏的理解. 一些名词与约定: 游戏:这里的游戏指的并不是平时玩的那些游戏(Dota2啥的),而是只一些如Nim取石子之类的“益智”组合游戏.并且,我们关注的不是游戏好不好玩,而是游戏有没有必胜策略.下文详细介绍. 状态:用一些数字来表

PyQt挖地雷游戏学习笔记(7)

1.游戏规则 地雷随机埋设在"棋盘"方格里,挖到地雷为败,挖光全部无雷方格为胜. 2.游戏的空间表示 游戏发生在棋盘上,游戏的场景.规则,都体现在棋盘上. 棋盘,由"场景盘"和"逻辑盘"共同组成. "场景盘"是玩家挖雷面对的棋盘. "逻辑盘"是实现游戏规则所需的"雷区盘"."空区盘"和"提示盘". "逻辑盘"由数组表示. 棋盘.

PyQt挖地雷游戏学习笔记(6)

学习别人的作品,有一大好处,可以反观自己的不足. 自己的不足,往往是基础知识有欠缺,基本功不扎实. 今天,再补一课:星号表达式(*expression). 挖地雷程序中,有2处用到星号表达式.一是在main.py,另一处在game_scene.py. 先看第一处的情况: @pyqtSlot() def on_action_Setup_triggered(self): result = setup.getSetup() if not result: return self.scene.setMap

PyQt挖地雷游戏学习笔记(2)

分析多个文件组成的源代码,最好使用辅助工具. 我试过Source Navigator 和 Source Insight,感觉后者功能多些,比较好用. 一.程序的组成模块等文件 这个挖地雷游戏,主要由2种文件组成: 1.程序模块 主程序main.py:负责主窗口物件,按钮和菜单的设置.事件处理.程序初始化.结束等等. 游戏逻辑模块game_scene.py:负责游戏规则.棋盘显示.胜负裁判.记录得分等等. "导入"文件lib.py:导入一些通用的模块,其他文件只需导入lib.py. 游戏

PyQt挖地雷游戏学习笔记(4)

又遇到一个有意思的问题,它提醒了查阅文档和相关源码,与测试的重要性. 直接上代码,setup.py """ module: setup """ from lib import * from config import * form, base = loadUiType("setup.ui") class SetupDlg(QDialog, form): def __init__(self): super(SetupDlg, s

sql 学习笔记 文档

以下内容来自 3c   school 1:Sql 分为两个部分: 6 2:查询 7 3:插入: 9 4:数据库更新 UPDATE 9 5:删除 DELETE 10 6:Sql TOP 子句: 10 7: SQL LIKE 操作符 11 8:SQL 通配符 13 2 使用 % 通配符 13 2 使用 _ 通配符 14 例子 1 14 例子 2 14 2 使用 [charlist] 通配符 15 例子 1 15 例子 2 15 9:IN 操作符 15 10: BETWEEN 操作符 16 11:Al

PyQt挖地雷游戏学习笔记(3)

这个程序使用了Python有特色的语法套路.先看第一个. main.py里的class MainWindow的函数init()中,有这样的语句: self.scene.setMap((conf.w, conf.h), conf.mines) 这里的变量conf,定义在config.py中,由以下语句引入的: from config import * 这里的conf.w和conf.h是什么呢?由此,转入config.py,一探究竟. #module: config import json DEFA

linux 学习笔记-文档的合并与归档

>           覆盖 >>        追加 归档(archiving)就是将许多文件(或目录)打包成一个文件 tar命令的常用选项:c:创建一个新的tar文件.t:列出tar文件中目录的内容.x:从tar文件中抽取文件.f:指定归档文件或磁带(也可能是软盘)设备(一般都要选).v:显示所打包的文件的详细信息,v是verbose的第1个字母.z:使用gzip压缩算法来压缩打包后的文件.j:使用bzip2压缩算法来压缩打包后的文件. 打包 :tar cf test.tar tes

Spring3.0学习笔记文档的官方网站(六)--3.4.1

3.4 依靠 3.4.1 依赖注入 依赖注入两种方式:基于构造函数DI.基于setter方法DI. 3.4.1.1 基于构造函数DI 参数是引进一个对象的.和缺乏父母之前-子类关系: package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } } <beans> <bean id="foo" class="x.y.Foo"> <constructor-a