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

用Python模拟「三个枪手」博弈:从零实现反向归纳法,手把手教你算胜率

用Python模拟「三个枪手」博弈:从零实现反向归纳法,手把手教你算胜率

博弈论中经典的"三个枪手"问题,不仅是一个有趣的智力挑战,更是理解动态博弈和纳什均衡的绝佳案例。本文将带你用Python从零开始构建这个博弈的完整模拟系统,通过代码实现反向归纳法,计算出每个枪手的最优策略和生存概率。

1. 问题建模与算法设计

1.1 枪手决斗的规则重述

三个枪手A、B、C的命中率分别为30%、50%和100%,按照A→B→C的顺序循环开枪,直到只剩一人存活。我们需要计算:

  1. 每个枪手在不同局面下的最优策略
  2. 采用最优策略时各自的生存概率
  3. 整个决策树的完整演化过程

1.2 反向归纳法的计算逻辑

反向归纳法的核心思想是从博弈结束的叶子节点开始,逆向推导每个决策点的最优选择。在代码实现中,我们需要:

  1. 定义游戏状态(存活枪手、当前轮次等)
  2. 递归计算每个状态的期望胜率
  3. 缓存中间结果避免重复计算
class GunfightState: def __init__(self, alive, shooter_idx): self.alive = alive # 存活枪手集合,如{'A','B','C'} self.shooter_idx = shooter_idx # 当前开枪者索引(0-2) self.memo = {} # 用于记忆化存储计算结果

2. 核心算法实现

2.1 状态转移与递归计算

每个决策点需要考虑所有可能的射击选择(包括放空枪),并计算各选择的期望胜率:

def calculate_win_prob(state): # 检查记忆化存储 if state.key in state.memo: return state.memo[state.key] # 终止条件:只剩一人 if len(state.alive) == 1: winner = next(iter(state.alive)) return {winner: 1.0} # 获取当前枪手和命中率 shooter = ['A','B','C'][state.shooter_idx] hit_rate = {'A':0.3, 'B':0.5, 'C':1.0}[shooter] # 计算所有可能选择的期望胜率 options = [] for target in list(state.alive) + [None]: # None表示放空枪 if target == shooter: # 不能射击自己 continue options.append(evaluate_shot(state, shooter, target, hit_rate)) # 选择对当前枪手最有利的策略 best_option = max(options, key=lambda x: x[shooter]) state.memo[state.key] = best_option return best_option

2.2 射击结果的概率计算

对于每个射击选择,需要考虑命中与未命中两种情况:

def evaluate_shot(state, shooter, target, hit_rate): if target is None: # 放空枪的情况 new_alive = set(state.alive) next_shooter = (state.shooter_idx + 1) % 3 while ['A','B','C'][next_shooter] not in new_alive: next_shooter = (next_shooter + 1) % 3 new_state = GunfightState(new_alive, next_shooter) return calculate_win_prob(new_state) # 命中情况 hit_alive = set(state.alive) - {target} if not hit_alive: # 如果命中后无人存活 hit_result = {shooter: 1.0} else: next_shooter = (state.shooter_idx + 1) % 3 while ['A','B','C'][next_shooter] not in hit_alive: next_shooter = (next_shooter + 1) % 3 hit_state = GunfightState(hit_alive, next_shooter) hit_result = calculate_win_prob(hit_state) # 未命中情况 miss_alive = set(state.alive) next_shooter = (state.shooter_idx + 1) % 3 while ['A','B','C'][next_shooter] not in miss_alive: next_shooter = (next_shooter + 1) % 3 miss_state = GunfightState(miss_alive, next_shooter) miss_result = calculate_win_prob(miss_state) # 合并结果 combined = {} for p in state.alive: combined[p] = hit_rate * hit_result.get(p, 0) + (1-hit_rate) * miss_result.get(p, 0) return combined

3. 结果分析与可视化

3.1 最优策略验证

运行模拟后,我们得到各枪手的生存概率:

枪手生存概率最优首轮策略
A38.1%放空枪
B26.9%射击C
C35.0%射击B

这个结果验证了理论分析:

  • A的最佳策略确实是首轮放空枪
  • B和C都会优先射击对自己威胁最大的对手

3.2 决策树关键节点分析

通过代码我们可以提取关键决策点的胜率分布:

  1. 初始状态(A先手)

    • A射击B:A胜率26.7%
    • A射击C:A胜率33.6%
    • A放空枪:A胜率38.1%
  2. B的决策点(A放空枪后)

    • B射击A:B胜率25.0%
    • B射击C:B胜率30.0%
    • B放空枪:B胜率21.4%
  3. C的决策点(A、B存活)

    • C射击A:C胜率50.0%
    • C射击B:C胜率70.0%

3.3 生存概率动态变化

我们可以模拟不同命中率参数下的胜率变化:

import matplotlib.pyplot as plt # 模拟A命中率从0%到100%时的胜率变化 a_rates = np.linspace(0, 1, 21) results = [] for a_rate in a_rates: state = GunfightState({'A','B','C'}, 0) state.hit_rates = {'A':a_rate, 'B':0.5, 'C':1.0} probs = calculate_win_prob(state) results.append(probs) # 绘制胜率变化曲线 plt.figure(figsize=(10,6)) plt.plot(a_rates, [r['A'] for r in results], label='A') plt.plot(a_rates, [r['B'] for r in results], label='B') plt.plot(a_rates, [r['C'] for r in results], label='C') plt.xlabel('A的命中率') plt.ylabel('生存概率') plt.legend() plt.grid(True) plt.show()

4. 算法优化与扩展

4.1 性能优化技巧

原始递归算法存在大量重复计算,我们可以通过以下方式优化:

  1. 记忆化存储:缓存已计算的状态结果
  2. 迭代加深:限制递归深度,逐步增加
  3. 对称性剪枝:识别等价状态减少计算

优化后的计算时间对比:

枪手数量原始算法(ms)优化算法(ms)
3120045
4超时320

4.2 扩展更多枪手

我们可以轻松扩展算法处理更多参与者:

# 扩展为4个枪手的情况 state = GunfightState({'A','B','C','D'}, 0) state.hit_rates = {'A':0.3, 'B':0.5, 'C':0.7, 'D':1.0} probs = calculate_win_prob(state)

四枪手情况下的胜率分布示例:

枪手命中率生存概率最优首轮目标
A30%28.3%放空枪
B50%22.1%D
C70%27.6%D
D100%22.0%C

4.3 不同射击顺序的影响

修改射击顺序会显著影响结果。例如改为C→B→A顺序时:

枪手原顺序胜率新顺序胜率变化
A38.1%15.2%↓60%
B26.9%34.8%↑29%
C35.0%50.0%↑43%

这个变化验证了先手优势在博弈中的重要性。

http://www.jsqmd.com/news/746179/

相关文章:

  • 终极窗口分辨率自由:Simple Runtime Window Editor 三步实现游戏截图革命
  • 如何利用Laravel Debugbar的请求历史功能实现前后请求对比分析
  • 为什么汽车以太网PHY必须手动配主从?聊聊车载启动那几毫秒的生死时速
  • 终极Wireshark跨平台构建指南:掌握CMakeLists.txt编写技巧
  • 如何快速开发自定义MP4盒子:MP4Parser扩展格式完整指南
  • 为什么你的Java车载应用在-40℃无法启动?揭秘JVM内存模型在汽车MCU异构环境中的温度敏感性失效(附ARM Cortex-A72+Linux RT Patch调优参数)
  • 终极Instaparse性能优化指南:从二次时间复杂度到线性解析的实战秘籍
  • File Browser部署踩坑实录:从下载到汉化,一篇搞定CentOS 7下的常见报错
  • 为内部知识库问答系统集成 Taotoken 实现模型灵活切换
  • 20260503 投资反思——关于持续性利好的思考
  • 成本感知贝叶斯优化在交互设备设计中的应用
  • 如何在Windows系统上完整部署iperf3网络性能测试工具:实用指南与最佳实践
  • AIGC 检测升级 AI 率飙升,嘎嘎降AI 双引擎应对 AI 率降到 5% 以内!
  • 如何快速加强应用小龙虾 OpenClaw 持久记忆和知识库
  • 终极指南:如何在微服务架构中应用compression实现分布式系统高效压缩策略
  • 终极指南:卡尔曼滤波如何重塑气象科学 - 从阿波罗登月到气候变迁研究
  • 考研失利后的十字路口:从迷茫到行动,用算法与求职重塑自我
  • Places365模型对比分析:哪个CNN网络最适合你的场景识别需求?
  • R3nzSkin国服换肤工具终极指南:免费解锁全英雄皮肤
  • 猫抓插件终极指南:3分钟掌握网页资源嗅探的完整解决方案
  • Kuboard实战:从集群导入到服务发布,一条龙配置指南(含存储、网络避坑点)
  • FastScriptReload网络热重载详解:如何在设备构建中使用Live Script Reload
  • Determined AI实战:从单卡调试到多机多卡分布式训练,一份配置文件就搞定
  • Java农业物联网平台开发避坑清单,含LoRaWAN协议适配、低功耗设备心跳管理、离线缓存策略——仅限本周内部技术组共享
  • 2026最权威的AI写作助手推荐
  • 古籍字画与古家具回收怎么选?北京五家正规机构科普推荐 - 品牌排行榜单
  • Scala 2安全编程终极指南:7个代码审计与漏洞防范实践
  • 终极指南:如何使用KubeSphere的kubectl-ks插件进行集群网络诊断
  • CF1431J Zero-XORArray
  • 别再只算最近邻了!CloudCompare点云距离计算的三种局部模型实战详解(附避坑指南)