讲师中心 微信公众号

工具推荐

视频效率加速
Seko
Seko

AI短剧一键生成成片

剧云
剧云

中文短剧剧本制作平台

Laper
Laper

剧本策划到成片协作

updream

一键生成高质量推广成片

Python囚徒困境模拟:优化锦标赛设置以准确评估策略表现

阿枫吖_7031

阿枫吖_7031

发布时间:2025-12-08 18:16:16

|

615人浏览过

|

来源于php中文网

原创

Python囚徒困境模拟:优化锦标赛设置以准确评估策略表现

本文深入探讨了python实现囚徒困境模拟时,策略表现与理论预期不符的问题。通过分析锦标赛配对机制,我们发现原始代码的循环逻辑导致同类型策略无法相互对抗,从而人为地提升了某些策略(如“总是背叛”)的得分。文章提供了修正后的代码,并解释了为何正确的配对方式对于准确评估“以牙还牙”等策略的真实效能至关重要,旨在帮助开发者构建更严谨的博弈论模拟。

理解囚徒困境与策略模拟

囚徒困境是博弈论中的一个经典模型,描述了两个理性个体在无法沟通的情况下,为了自身利益最大化而选择背叛,最终导致集体利益受损的困境。在迭代囚徒困境中,玩家会进行多轮游戏,并根据历史记录调整策略。常见的策略包括:

  • 总是合作 (Always Cooperate):无论对手做什么,都选择合作。
  • 总是背叛 (Always Defect):无论对手做什么,都选择背叛。
  • 以牙还牙 (Tit for Tat):第一轮合作,之后模仿对手上一轮的行动。
  • 以牙还两牙 (Tit for Two Tats):第一轮合作,只有当对手连续两轮背叛时才背叛。
  • 随机选择 (Random Choice):根据一定概率选择合作或背叛。

模拟这些策略在多轮锦标赛中的表现,是评估其有效性的重要方法。通常,在迭代囚徒困境中,“以牙还牙”策略被认为是表现最优异的策略之一,因为它兼具善良(从不首先背叛)、报复(对背叛行为进行惩罚)和宽恕(一旦对手合作便重新合作)的特点。

原始模拟结果的困惑

在初始的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

从结果来看,“总是背叛”和“随机选择背叛”策略的得分明显高于“以牙还牙”和“总是合作”策略,这表明模拟环境可能存在某种偏向。

立即学习“Python免费学习笔记(深入)”;

问题诊断:锦标赛配对机制

经过仔细检查,问题根源在于锦标赛(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]进行比赛。这意味着:

  1. 没有同类型玩家之间的对抗:如果锦标赛中只有一种“总是背叛”策略实例,那么它将永远不会遇到另一个“总是背叛”策略。
  2. 优势策略的稀缺性:当“总是背叛”策略在玩家池中是稀有类型时,它总能从与合作型策略的对抗中获得高分(合作者得0分,背叛者得5分),而无需承担与另一个背叛者对抗(双方各得1分)的风险。这种不对称的配对机制为人为地提升了背叛策略的平均得分。

在一个公平的锦标赛环境中,所有玩家类型都应该有机会与其他所有玩家类型进行对抗,包括与自身类型的对抗。例如,一个“总是背叛”策略应该有机会与另一个“总是背叛”策略进行比赛,这样才能准确反映其在不同环境下的表现。

解决方案:修正锦标赛配对逻辑

要解决上述问题,只需修改锦标赛中的内部循环范围,使其包含同类型玩家之间的对抗。将j的起始索引从i+1改为i:

Python Hardener
Python Hardener

通过完整的安全审查、代码整洁重构、健壮的错误处理、正确的日志记录以及完整的文档字符串覆盖,强化 Python 脚本。

下载
for i in range(len(players)):
    for j in range(i, len(players)): # 修正后的循环范围
        player1 = players[i]
        player2 = players[j]
        # ... 进行比赛 ...

这个简单的改动确保了:

  • 当i == j时,player1会与player2(即它自己)进行比赛。这意味着如果玩家列表中包含多个相同策略的实例(例如,两个always_defect),它们将相互对抗。即使玩家列表中只有一个always_defect实例,它也会与自己进行比赛,这在计算平均得分时是合理的。
  • 所有可能的玩家对都会被考虑,包括策略类型与自身类型的对抗。

修正后的 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分)。相反,“以牙还牙”等策略的相对表现可能会提升,因为它在与合作者对战时表现良好,同时也能有效惩罚背叛者,并在对手重新合作时给予宽恕。

为什么“以牙还牙”通常表现出色?

  • 善良 (Nice):它从不首先背叛,这有助于建立合作关系。
  • 报复 (Retaliatory):它会立即对背叛行为进行惩罚,阻止对手利用自己。
  • 宽恕 (Forgiving):一旦对手恢复合作,它也会立即恢复合作,避免陷入长期僵局。
  • 清晰 (Clear):它的行为模式简单易懂,对手很容易预测并适应。

这些特性使得“以牙还牙”在迭代囚徒困境中具有很强的鲁棒性,尤其是在玩家数量较多且对局次数足够多的情况下。

注意事项与进一步思考

  1. 玩家实例与类型:在上述代码中,players列表中的是策略函数本身,这意味着我们模拟的是不同“类型”的玩家。如果需要模拟多个相同“类型”但不同“实例”的玩家(例如,两个独立的“以牙还牙”玩家),则需要调整players列表的构建方式,例如players = [tit_for_tat, tit_for_tat, always_defect, ...]。在这种情况下,修正后的循环for j in range(i, len(players))将正确地让两个tit_for_tat实例相互对抗。
  2. 锦标赛设计:一个健壮的囚徒困境模拟锦标赛应该确保所有策略都有机会与所有其他策略(包括自身)进行充分的交互。
  3. 回合数与随机性:迭代回合数(rounds)和重复锦标赛次数(num_tournaments)的设定对结果的稳定性和代表性至关重要。足够多的回合数可以体现迭代策略的长期效果,而多次锦标赛可以减少随机因素的影响。
  4. 环境因素:囚徒困境的最优策略并非一成不变,它会受到环境(如博弈轮数是否已知、玩家是否能识别对手、收益矩阵的具体数值)的影响。本教程主要关注了模拟设置的公平性。

总结

在博弈论模拟中,一个微小的代码细节可能导致结果与理论预期大相径庭。本例中,Python囚徒困境模拟的配对机制缺陷,导致“总是背叛”策略因缺乏同类对抗而表现异常优异。通过将锦标赛的内部循环从range(i+1, len(players))修正为range(i, len(players)),并注意分数累加的公平性,我们能够创建一个更公平、更准确的模拟环境,从而更真实地反映“以牙还牙”等策略在迭代囚徒困境中的实际效能。这强调了在设计和实现复杂模拟时,对每一个细节进行严谨考量的重要性。

热门AI工具

更多
超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

相关专题

更多
Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

0

2026.11.12

Go-Zero安装与goctl环境配置教程
Go-Zero安装与goctl环境配置教程

本专题围绕Go-Zero的安装与工具链配置展开,讲清Go版本要求、goctl安装方式、代理镜像、protoc依赖与环境变量设置,说明多版本共存、升级回退与CI流水线安装的做法,并整理安装阶段的常见报错与验证步骤。

0

2026.11.12

Go-Zero零基础入门教程
Go-Zero零基础入门教程

本专题整理Go-Zero的入门知识,从框架定位、环境准备到跑通第一个API服务,讲清目录分层、配置加载、日志与中间件的基本用法,并对比Gin、Kratos的差异,帮助Go开发者建立整体认知,判断它是否适合当前项目。

0

2026.11.12

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

120

2026.11.06

Postman请求头Headers怎么设置
Postman请求头Headers怎么设置

围绕Postman HTTP请求头配置展开,讲解Headers选项的具体使用方式,包括Key和Value参数填写、请求头信息添加、常见Header字段设置及请求头修改方法,帮助用户解决接口调试过程中遇到的请求头配置问题。

60

2026.11.06

Postman接口请求发送教程
Postman接口请求发送教程

详细介绍Postman接口请求的创建与发送方法,涵盖GET和POST请求、URL配置、请求参数、Headers请求头、Body请求体设置以及响应结果查看,帮助用户掌握Postman接口请求的完整操作流程。

80

2026.11.06

DBeaver怎么导入数据
DBeaver怎么导入数据

DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。

80

2026.11.05

DBeaver安全防护教程大全
DBeaver安全防护教程大全

DBeaver安全防护专题,围绕数据库连接安全、账号密码保护和访问权限管理展开,整理SSL/TLS加密连接、SSH隧道、数据库凭据保存及证书验证等配置方法,同时介绍敏感信息保护、远程连接风险和高权限账号使用等注意事项,帮助用户提高数据库日常管理的安全性。

60

2026.11.05

DBeaver连接创建与配置教程
DBeaver连接创建与配置教程

本专题聚焦DBeaver数据库连接配置与管理,覆盖从新建连接、选择数据库类型到填写连接参数和验证连接的完整流程。针对MySQL、PostgreSQL、SQL Server等常见数据库,整理相应的连接设置方法,并提供驱动缺失、认证失败和连接异常等问题的排查方向。

60

2026.11.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn