本文深入探讨了python实现囚徒困境模拟时,策略表现与理论预期不符的问题。通过分析锦标赛配对机制,我们发现原始代码的循环逻辑导致同类型策略无法相互对抗,从而人为地提升了某些策略(如“总是背叛”)的得分。文章提供了修正后的代码,并解释了为何正确的配对方式对于准确评估“以牙还牙”等策略的真实效能至关重要,旨在帮助开发者构建更严谨的博弈论模拟。
囚徒困境是博弈论中的一个经典模型,描述了两个理性个体在无法沟通的情况下,为了自身利益最大化而选择背叛,最终导致集体利益受损的困境。在迭代囚徒困境中,玩家会进行多轮游戏,并根据历史记录调整策略。常见的策略包括:
模拟这些策略在多轮锦标赛中的表现,是评估其有效性的重要方法。通常,在迭代囚徒困境中,“以牙还牙”策略被认为是表现最优异的策略之一,因为它兼具善良(从不首先背叛)、报复(对背叛行为进行惩罚)和宽恕(一旦对手合作便重新合作)的特点。
在初始的Python模拟实现中,我们观察到“总是背叛”和“随机选择背叛”等策略获得了较高的总分,而“以牙还牙”等合作倾向策略的得分相对较低。这与博弈论中关于迭代囚徒困境的常见结论(即“以牙还牙”往往表现优异)产生了偏差。以下是原始模拟的部分输出结果示例:
always_cooperate: 1347.024 coins random_choice_cooperate: 1535.651 coins tit_for_two_tats: 1561.442 coins tit_for_tat: 1609.444 coins tat_for_tit: 1619.43 coins random_choice_neutral: 1663.855 coins always_defect: 1711.764 coins random_choice_defect: 1726.992 coins
从结果来看,“总是背叛”和“随机选择背叛”策略的得分明显高于“以牙还牙”和“总是合作”策略,这表明模拟环境可能存在某种偏向。
经过仔细检查,问题根源在于锦标赛(tournament)函数的配对逻辑。原始代码使用以下循环结构来配对玩家:
for i in range(len(players)):
for j in range(i+1, len(players)):
player1 = players[i]
player2 = players[j]
# ... 进行比赛 ...这段代码的含义是,对于列表中的每个玩家player[i],它只会与列表后面(索引大于i)的玩家player[j]进行比赛。这意味着:
在一个公平的锦标赛环境中,所有玩家类型都应该有机会与其他所有玩家类型进行对抗,包括与自身类型的对抗。例如,一个“总是背叛”策略应该有机会与另一个“总是背叛”策略进行比赛,这样才能准确反映其在不同环境下的表现。
要解决上述问题,只需修改锦标赛中的内部循环范围,使其包含同类型玩家之间的对抗。将j的起始索引从i+1改为i:
for i in range(len(players)):
for j in range(i, len(players)): # 修正后的循环范围
player1 = players[i]
player2 = players[j]
# ... 进行比赛 ...这个简单的改动确保了:
修正后的 tournament 函数示例:
import random
from colorama import Fore, Style
import numpy as np
# ... (其他策略和常量定义保持不变) ...
# Define the payoff matrix
payoff_matrix = {
(COOPERATE, COOPERATE): (3, 3),
(COOPERATE, DEFECT): (0, 5),
(DEFECT, COOPERATE): (5, 0),
(DEFECT, DEFECT): (1, 1)
}
# Define the players
players = [always_cooperate, always_defect, random_choice_defect, tit_for_tat, tit_for_two_tats, random_choice_cooperate, tat_for_tit, random_choice_neutral]
# ... (player_colors 定义保持不变) ...
def tournament(players, rounds=100):
total_scores = {player.__name__: 0 for player in players}
for i in range(len(players)):
for j in range(i, len(players)): # 修正此处:将 i+1 改为 i
player1 = players[i]
player2 = players[j]
history1 = []
history2 = []
match_scores = {player1.__name__: 0, player2.__name__: 0}
for round_num in range(rounds): # round 变量名与函数参数冲突,改为 round_num
move1 = player1(history1)
move2 = player2(history2)
score1, score2 = payoff_matrix[(move1, move2)]
# 累加到本场比赛得分
match_scores[player1.__name__] += score1
match_scores[player2.__name__] += score2
# 累加到总得分 (注意:这里需要确保公平性,如果 i == j,则只加一次)
total_scores[player1.__name__] += score1
if i != j: # 只有在不同玩家对战时,才为 player2 额外累加一次
total_scores[player2.__name__] += score2
else: # 如果是同一玩家对战,则 player2 就是 player1,score2 已经通过 score1 累加了
pass # 实际player1和player2是同一个策略实例,score2就是score1,已在player1处累加
history1.append((move1, move2))
history2.append((move2, move1))
sorted_scores = sorted(total_scores.items(), key=lambda item: item[1], reverse=True)
return sorted_scores
# ... (后续运行和打印结果的代码保持不变) ...关于分数累加的注意事项:
在tournament函数中,当i == j时,player1和player2实际上是同一个策略(例如,always_defect与always_defect)。在这种情况下,score1和score2是相同的,且player1.__name__和player2.__name__也是相同的。原始代码的total_scores[player1.__name__] += score1和total_scores[player2.__name__] += score2会导致分数被重复计算。
为了避免重复计算,可以调整分数累加逻辑:
# ... (tournament 函数内部) ...
for round_num in range(rounds):
move1 = player1(history1)
move2 = player2(history2)
score1, score2 = payoff_matrix[(move1, move2)]
# 累加到本场比赛得分
match_scores[player1.__name__] += score1
match_scores[player2.__name__] += score2
# 累加到总得分
total_scores[player1.__name__] += score1
# 只有当 player1 和 player2 是不同策略实例时,才为 player2 累加分数
# 否则,player2 的分数已经通过 player1 累加了
if player1.__name__ != player2.__name__:
total_scores[player2.__name__] += score2
history1.append((move1, move2))
history2.append((move2, move1))
# ... (tournament 函数外部) ...或者,更简洁且确保每个玩家的总分是其在所有对局中得分之和的方式是,在外部循环结束后,将match_scores累加到total_scores中,而不是在内部循环中直接累加。但这需要更彻底的重构,因为原始设计是在每个回合累加。 对于当前的结构,最直接的修正就是确保当player1和player2是同一个策略实例时,其总分不会被加倍。
通过修正配对逻辑,锦标赛将更公平地评估每个策略。在这种公平的竞争环境下,我们预期“总是背叛”策略的平均得分会下降,因为它现在必须承担与同样选择背叛的对手对抗的风险(双方各得1分,而非背叛者得5分)。相反,“以牙还牙”等策略的相对表现可能会提升,因为它在与合作者对战时表现良好,同时也能有效惩罚背叛者,并在对手重新合作时给予宽恕。
为什么“以牙还牙”通常表现出色?
这些特性使得“以牙还牙”在迭代囚徒困境中具有很强的鲁棒性,尤其是在玩家数量较多且对局次数足够多的情况下。
在博弈论模拟中,一个微小的代码细节可能导致结果与理论预期大相径庭。本例中,Python囚徒困境模拟的配对机制缺陷,导致“总是背叛”策略因缺乏同类对抗而表现异常优异。通过将锦标赛的内部循环从range(i+1, len(players))修正为range(i, len(players)),并注意分数累加的公平性,我们能够创建一个更公平、更准确的模拟环境,从而更真实地反映“以牙还牙”等策略在迭代囚徒困境中的实际效能。这强调了在设计和实现复杂模拟时,对每一个细节进行严谨考量的重要性。
相关文章:
建站之星客服服务时间及联系方式如何?
宁波自助建站系统如何快速打造专业企业网站?
深入理解Android中的xmlns:tools属性
网站建设制作、微信公众号,公明人民医院怎么在网上预约?
如何访问已购建站主机并解决登录问题?
php条件判断怎么写_ifelse和switchcase的使用区别【对比】
建站主机默认首页配置指南:核心功能与访问路径优化
清除minerd进程的简单方法
如何通过PHP快速构建高效问答网站功能?
建站之星如何快速更换网站模板?
建站之星展会模板:智能建站与自助搭建高效解决方案
c++23 std::expected怎么用 c++优雅处理函数错误返回【详解】
建站之星如何快速解决建站难题?
建站主机选哪种环境更利于SEO优化?
如何自定义建站之星网站的导航菜单样式?
seo网站制作优化,网站SEO优化步骤有哪些?
c# 在高并发场景下,委托和接口调用的性能对比
建站之星如何保障用户数据免受黑客入侵?
建站之星后台密码遗忘如何找回?
如何基于云服务器快速搭建网站及云盘系统?
设计网站制作公司有哪些,制作网页教程?
海南网站制作公司有哪些,海口网是哪家的?
电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?
手机网站制作与建设方案,手机网站如何建设?
如何规划企业建站流程的关键步骤?
如何选择高效可靠的多用户建站源码资源?
如何快速搭建安全的FTP站点?
建站DNS解析失败?如何正确配置域名服务器?
如何快速查询网址的建站时间与历史轨迹?
宝华建站服务条款解析:五站合一功能与SEO优化设置指南
义乌企业网站制作公司,请问义乌比较好的批发小商品的网站是什么?
零基础网站服务器架设实战:轻量应用与域名解析配置指南
,怎么用自己头像做动态表情包?
宝塔建站教程:一键部署配置流程与SEO优化实战指南
香港服务器租用费用高吗?如何避免常见误区?
Python文件管理规范_工程实践说明【指导】
定制建站方案优化指南:企业官网开发与建站费用解析
建站之星导航配置指南:自助建站与SEO优化全解析
如何在IIS中新建站点并配置端口与物理路径?
网站制作专业公司有哪些,如何制作一个企业网站,建设网站的基本步骤有哪些?
如何在云服务器上快速搭建个人网站?
制作网站的公司有哪些,做一个公司网站要多少钱?
大连网站设计制作招聘信息,大连投诉网站有哪些?
山东云建站价格为何差异显著?
建站之星伪静态规则如何设置?
如何用y主机助手快速搭建网站?
如何高效搭建专业期货交易平台网站?
如何在宝塔面板中创建新站点?
建站之星3.0如何解决常见操作问题?
专业公司网站制作公司,用什么语言做企业网站比较好?
*请认真填写需求信息,我们会在24小时内与您取得联系。