当前位置: 首页 > news >正文

Wythoff Game

模型介绍

Wythoff Game 是一个经典的二人博弈游戏,规则如下:

  • 有两堆物品,分别有 \(a\)\(b\) 个;
  • 两名玩家轮流操作,每次可以:
  1. 从其中一堆取任意数量的物品(至少 \(1\) 个);
  2. 从两堆中同时取相同数量的物品(至少 \(1\) 个);
  • 最后取光所有物品的玩家获胜。

历史背景

Wythoff Game 由荷兰数学家 Willem Abraham Wythoff 在 \(1907\) 年提出。这个游戏是组合博弈论中的重要模型,其解与黄金比例有着深刻的联系。

核心结论与证明

奇异局势(必败态)

Wythoff Game 存在一系列的必败位置,称为"奇异局势"。这些位置可以表示为:

\[(a_k,b_k)=(\lfloor k\varphi\rfloor,\lfloor k\varphi^2\rfloor)=(\lfloor k\varphi\rfloor,\lfloor k\varphi\rfloor+k) \]

其中 \(\varphi=\frac{1+\sqrt5}{2}\approx 1.61803\) 是黄金比例,\(k=0,1,2,\cdots\)

前几个奇异局势:

  • \((0,0)\)
  • \((1,2)\)
  • \((3,5)\)
  • \((4,7)\)
  • \((6,10)\)
  • \((8,13)\)
  • \((9,15)\)
  • \(\cdots\)

判断方法

对于任意位置 \((a,b)\)(假设 \(a\le b\)):

  • 计算 \(k=b-a\)
  • 计算 \(a_k=\lfloor k\varphi\rfloor\)
  • 如果 \(a=a_k\),则该位置是奇异局势(先手必败);
  • 否则,先手必胜。

证明

Beatty 定理背景:

如果 \(\alpha\)\(\beta\) 是正无理数且满足 \(\frac{1}{\alpha}+\frac{1}{\beta}=1\),那么序列 \(\lfloor n\alpha\rfloor\)\(\lfloor n\beta\rfloor\) 恰好覆盖所有正整数且不相交。

构造证明:

  1. \(\alpha=\varphi,\beta=\varphi²\)
  2. 由于 \(\varphi\)\(\varphi^2\) 满足 \(\frac{1}{\varphi}+\frac{1}{\varphi^2}=1\)
  3. 根据 Beatty 定理,序列 \(\lfloor k\varphi\rfloor\)\(\lfloor k\varphi^2\rfloor\) 覆盖所有正整数;
  4. \(\lfloor k\varphi^2\rfloor=\lfloor k\varphi+k\rfloor=\lfloor k\varphi\rfloor+k\)

转移性质证明:

从任意奇异局势出发:

  • 如果只取一堆:会破坏 Beatty 序列的覆盖性质;
  • 如果同时取两堆:差值 \(k\) 不变,但较小的数会改变。

从任意非奇异局势出发:

  • 总能通过一次操作到达奇异局势。

代码实现

基础判断

#include <bits/stdc++.h>
#define int long longusing namespace std;const double kPhi = (1.0 + sqrt(5.0)) / 2.0;// 判断是否为奇异局势(必败态)
bool isLosingPosition(int a, int b) {if (a > b) swap(a, b);int k = b - a;int ak = floor(k * kPhi);return a == ak;
}// 获取必胜策略
void findWinningMove(int a, int b, int& take_from, int& take_count) {if (a > b) swap(a, b);int k = b - a;int ak = floor(k * kPhi);int bk = ak + k;if (a == ak) {// 已经是奇异局势,没有必胜策略take_from = -1;take_count = -1;return;}if (a > ak) {// 从两堆同时取 (a - ak) 个take_from = 0;  // 0 表示同时取两堆take_count = a - ak;} else {// 需要调整使得到达奇异局势// 从较大的堆取,使得较小的堆等于某个奇异局势的第一分量for (int i = 0; i <= k; i++) {int candidate_ak = floor(i * kPhi);int candidate_bk = candidate_ak + i;if (a == candidate_ak && b > candidate_bk) {take_from = 2;  // 从第二堆取take_count = b - candidate_bk;return;}if (a == candidate_bk && b > candidate_ak) {take_from = 2;take_count = b - candidate_ak;return;}}// 如果上述方法不行,从任意一堆取1个take_from = 1;take_count = 1;}
}

完整游戏模拟

#include <bits/stdc++.h>
#define int long longusing namespace std;class WythoffGame {private:int pileA, pileB;const double PHI = (1.0 + sqrt(5.0)) / 2.0;public:WythoffGame(int a, int b) : pileA(a), pileB(b) {}bool isGameOver() {return pileA == 0 && pileB == 0;}bool isValidMove(int from, int count) {if (count <= 0) return false;if (from == 1) {  // 从第一堆取return count <= pileA;} else if (from == 2) {  // 从第二堆取return count <= pileB;} else if (from == 0) {  // 从两堆同时取return count <= min(pileA, pileB);}return false;}bool makeMove(int from, int count) {if (!isValidMove(from, count)) return false;if (from == 1) {pileA -= count;} else if (from == 2) {pileB -= count;} else if (from == 0) {pileA -= count;pileB -= count;}return true;}void printState() {cout << "当前状态: (" << pileA << ", " << pileB << ")" << endl;}// 电脑的智能移动void computerMove() {int from, count;findWinningMove(pileA, pileB, from, count);if (from == -1) {// 必败态,随机移动if (pileA > 0) {from = 1;count = 1;} else if (pileB > 0) {from = 2;count = 1;} else {from = 0;count = 0;}}makeMove(from, count);cout << "电脑: ";if (from == 0) {cout << "从两堆同时取 " << count << " 个" << endl;} else if (from == 1) {cout << "从第一堆取 " << count << " 个" << endl;} else {cout << "从第二堆取 " << count << " 个" << endl;}}
};void playWythoffGame() {WythoffGame game(15, 25);bool playerTurn = true;cout << "Wythoff Game 开始!" << endl;cout << "初始状态: (15, 25)" << endl;cout << "移动方式: 1-从第一堆取, 2-从第二堆取, 0-从两堆同时取" << endl;while (!game.isGameOver()) {game.printState();if (playerTurn) {int from, count;cout << "你的回合 - 输入移动方式(0,1,2)和数量: ";cin >> from >> count;if (!game.makeMove(from, count)) {cout << "无效移动!请重新输入。" << endl;continue;}} else {game.computerMove();}playerTurn = !playerTurn;}cout << "游戏结束!" << (playerTurn ? "电脑" : "玩家") << "获胜!" << endl;
}

变种题目与解法

变种 1:最后取者输

  • 规则:其他规则相同,但最后取物品的人输;
  • 解法:需要重新分析奇异局势。
bool isLosingPositionMisere(int a, int b) {if (a == 0 && b == 0) return false;  // 没有物品,上一个人已经输了if (a == 1 && b == 1) return true;   // 特殊情况// 其他情况与正常游戏类似,但需要调整return isLosingPosition(a, b);
}

变种 2:多堆 Wythoff Game

  • 规则:有 \(n\) 堆物品,每次可以:
  1. 从任意一堆取任意数量;
  2. 从所有堆中取相同数量;
  • 解法:使用 Grundy 数。
int calculateGrundy(int n) {vector<int> grundy(n + 1, 0);for (int i = 1; i <= n; i++) {set<int> moves;// 从当前堆取任意数量for (int take = 1; take <= i; take++) {moves.insert(grundy[i - take]);}// 从所有堆取相同数量(在单堆情况下无法表示)// 在多堆情况下需要特殊处理int g = 0;while (moves.count(g)) g++;grundy[i] = g;}return grundy[n];
}bool multiPileWythoff(vector<int>& piles) {int xor_sum = 0;for (int pile : piles) {xor_sum ^= calculateGrundy(pile);}return xor_sum != 0;
}

变种3:受限 Wythoff Game

  • 规则:每次取物品数量有限制;
  • 解法:动态规划。
bool limitedWythoff(int a, int b, int max_take) {vector<vector<bool>> dp(a + 1, vector<bool>(b + 1, false));for (int i = 0; i <= a; i++) {for (int j = 0; j <= b; j++) {// 从第一堆取for (int take = 1; take <= min(i, max_take); take++) {if (!dp[i - take][j]) {dp[i][j] = true;break;}}if (dp[i][j]) continue;// 从第二堆取for (int take = 1; take <= min(j, max_take); take++) {if (!dp[i][j - take]) {dp[i][j] = true;break;}}if (dp[i][j]) continue;// 从两堆同时取for (int take = 1; take <= min(i, j, max_take); take++) {if (!dp[i - take][j - take]) {dp[i][j] = true;break;}}}}return dp[a][b];
}

总结

Wythoff Game 是组合博弈论中的经典问题,其特点包括:

  1. 优美的数学结构:与黄金比例 \(\varphi\) 紧密相关;
  2. 完整的理论体系:有明确的必胜必败判定方法;
  3. 丰富的变种:可以衍生出多种有趣的博弈问题。
http://www.jsqmd.com/news/18539/

相关文章:

  • 在 PADS 中将修改的原理图元件电气信息更新到 PCB 的方法
  • TypeScript 高级类型工具:Partial, Required, Record 的妙用与陷阱
  • 阿里云EIP指标监控
  • 深入了解linux网络—— TCP网络通信(上) - 详解
  • Spark专题-第三部分:性能监控与实战优化(2)-分区优化 - 详解
  • 补贴防薅测试用例设计
  • 20232313 2025-2026-1 《网络与系统攻防技术》实验二实验报告 - 20232313
  • 理解C++20的革命特性——协程支持2:编写简单的协程调度器 - 实践
  • 站位4
  • 九种UML常见图 -2025.10.19
  • 阿里云 CDN部署
  • 分箱效果评估:IV值和卡方
  • 2025 年电缆桥架生产厂家最新推荐排行榜:聚焦北方 / 河北区域及瓦楞 / 防火 / 模压 / 镀锌桥架优质品牌深度解析
  • 洒水清洁,音乐相伴,洒水车声音-兰花草音乐芯片详细资料
  • 04.Python百行代码制作查询工具
  • 通过一台服务器采集所有阿里云账单费用数据
  • 2025 油烟机厂家最新推荐榜:五大实力厂商技术与服务口碑评测权威发布滑轨/易清洁/免清洗/智能油烟机厂家推荐
  • VUE---打印功能
  • 高效管理超多传感器?SHxxx 集线器实现精准切换与零混淆 告别通道混乱,内置校验
  • [ACTF2020 新生赛]Include 1 文件包含
  • 鸿蒙NEXT网络管理:从“能用”到“智能”的架构演进 - 指南
  • PostgreSQL可观测性完整方案
  • 2025 年通风天窗源头厂家最新推荐:品牌定制能力、售后体系及综合实力深度测评榜单
  • 钡铼技术全新APC系列工业边缘可视化平板电脑即将重磅发布!
  • 2025年大连甘井子区优质养老机构推荐:从社区到自然的暖心之选
  • 2025年AI营销公司推荐:广东AI营销公司/广州AI营销公司如何以模块化服务破解企业增长困局
  • 2025年越南货架厂家推荐榜:立体/高位/仓储/托盘/重型/流利式/贯通式/穿梭车/模具/货架厂家,多维度解析行业实力派
  • 2025年主轴维修厂家企业推荐: 电/高速/精密/磨床/进口磨床/加工中心电/数控机床/高速电主轴维修厂家,服务商助力制造企业降本增效
  • 2025年磨床电主轴先升级推荐榜:国产/进口/内圆/外圆/无心/平面/来图定制磨床电主轴厂家,聚焦精密制造核心
  • 在写left join的时候 是大表在左侧 还是小表在左侧(二)