博弈论,第二部分 – 好人先赢

原文:towardsdatascience.com/game-theory-part-2-nice-guys-finished-first-8cd9022a935f

与常见的“好人总是最后赢”的观点相反,博弈论揭示了好人确实可以首先获胜。我将在本文中通过迭代囚徒困境问题来探讨这个有趣的现象。这是我的博弈论系列文章的第二部分,所以如果你还没有阅读第一篇文章,我建议你先阅读。第一部分通过两位间谍克拉蒂卡和伊希塔的例子讨论了经典的囚徒困境问题,并说明了他们的决策如何导致双方的最优结果。它还强调了博弈论在许多现实世界场景中的相关性。第一篇文章的链接在这里 –

博弈论,第一部分 – 囚徒困境问题

迭代囚徒困境问题

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/339264adeb60450012055dc4abd24c6d.png

图片由 GrabUnsplash 提供

在现实生活中,各方之间的互动并不总是单次事件。当然,克拉蒂卡和伊希塔,这两位间谍,曾经面临过这个困境,最终在监狱里服刑 7 年。但在一个平行世界中,克拉蒂卡和伊希塔作为两家竞争的食品配送平台的 CEO,每天都要面对同样的困境(在定价方面)。两家公司的利润都在下降。他们的投资者对投资回报率(ROI)的下降越来越沮丧,并敦促 CEO 们采取行动。这个困境迫使克拉蒂卡和伊希塔重新思考他们的策略。在这个定价游戏中 –

  • 如果双方都保持他们的价格(或合作),他们各自将获得 3 个单位的利润。

  • 如果一方降低价格(或背叛)而另一方保持价格不变,那么降低价格的一方将获得 5 个单位,而保持价格的一方则一无所获。

  • 如果双方都降低价格,他们各自只能获得 1 个单位的利润。

如果他们每天通过降低价格(背叛)来玩这个游戏,双方都将只获得 1 个单位。这与他们保持价格(合作)时本可以获得的 3 个单位相比,是不理想的。克拉蒂卡和伊希塔都知道这一点。他们不会采取这种策略。

克拉蒂卡雇佣了几位博弈论专家,他们提出了她可以选择的策略。

  1. 总是合作 – 玩家无条件地合作。换句话说,玩家总是保持价格。

  2. 总是背叛 – 玩家无条件地背叛。换句话说,玩家降低价格。

  3. 随机 – 玩家在合作和背叛之间随机选择。

  4. 以牙还牙 – 玩家通过合作开始游戏,并在下一次行动中模仿对手的行动。

  5. 严厉触发 – 玩家开始时合作,但一旦对手第一次背叛,玩家将永久性地背叛。

  6. 反向以牙还牙 – 玩家开始游戏时背叛,并在下一轮中模仿对手的行动。

  7. 巴甫洛夫 – 如果上一轮的结果是双方都合作或背叛,玩家将合作。然而,如果结果不同(即一方合作而另一方背叛),玩家将改变他们在上一轮的选择。

  8. 先背叛后合作 – 玩家开始时背叛。如果对手合作,玩家将在下一轮合作,但如果对手再次背叛,玩家将回归背叛。

克拉蒂卡的循环赛模拟

克拉蒂卡决定模拟一场 8 名玩家参与的循环赛,这些玩家选择了上述 8 种策略。这样,测试了所有可能的策略组合。这些游戏进行了 200 轮。

克拉蒂卡为玩家创建了一个 Python 类,其中包含不同策略的方法。以下代码块执行了相同的功能。

import random

class Player:

    def __init__(self, strategy_name):
        self.strategy_name = strategy_name
        self.previous_moves = []
        self.cooperate = 1
        self.defect = 0
        self.total_profit = 0

    def move(self, opponent_previous_moves):
        if self.strategy_name == 'always_cooperate':
            return self.always_cooperate()
        elif self.strategy_name == 'always_defect':
            return self.always_defect()
        elif self.strategy_name == 'random':
            return self.random_choice()
        elif self.strategy_name == 'tit_for_tat':
            return self.tit_for_tat(opponent_previous_moves)
        elif self.strategy_name == 'grim_trigger':
            return self.grim_trigger(opponent_previous_moves)
        elif self.strategy_name == 'reverse_tit_for_tat':
            return self.reverse_tit_for_tat(opponent_previous_moves)
        elif self.strategy_name == 'pavlov':
            return self.pavlov(opponent_previous_moves)
        elif self.strategy_name == 'defect_until_cooperate':
            return self.defect_until_cooperate(opponent_previous_moves)
        elif self.strategy_name == 'tit_for_two_tats':
            return self.tit_for_two_tats(opponent_previous_moves)

    def calculate_profit(self, my_move, opponent_move):
        if my_move == self.cooperate and opponent_move == self.cooperate:
            return 3
        elif my_move == self.cooperate and opponent_move == self.defect:
            return 0
        elif my_move == self.defect and opponent_move == self.cooperate:
            return 5
        elif my_move == self.defect and opponent_move == self.defect:
            return 1

    def always_cooperate(self):
        return self.cooperate

    def always_defect(self):
        return self.defect

    def random_choice(self):
        return random.choice([self.cooperate, self.defect])

    def tit_for_tat(self, opponent_previous_moves):
        if len(opponent_previous_moves) == 0:
            return self.cooperate
        return opponent_previous_moves[-1]

    def grim_trigger(self, opponent_previous_moves):
        if self.defect in opponent_previous_moves:
            return self.defect
        return self.cooperate

    def reverse_tit_for_tat(self, opponent_previous_moves):
        if len(self.my_moves) == 0:
            return self.defect
        return opponent_previous_moves[-1]

    def pavlov(self, opponent_previous_moves):
        if len(self.my_moves) == 0:
            return self.cooperate
        if (self.my_moves[-1] == self.cooperate and opponent_previous_moves[-1] == self.cooperate) or 
           (self.my_moves[-1] == self.defect and opponent_previous_moves[-1] == self.defect):
            return self.cooperate
        else:
            return self.defect

    def defect_until_cooperate(self, opponent_previous_moves):
        if len(opponent_previous_moves) >= 1 and opponent_previous_moves[-1] == self.cooperate:
            return self.cooperate
        return self.defect

    def tit_for_two_tats(self, opponent_previous_moves):
        if len(opponent_previous_moves) >= 2 and opponent_previous_moves[-1] == self.defect and opponent_previous_moves[-2] == self.defect:
            return self.defect
        return self.cooperate

她为模拟编写了以下 Python 函数。

def simulate_game(strategy1, strategy2, rounds):
    player1 = Player(strategy1)
    player2 = Player(strategy2)

    for _ in range(rounds):
        p1_move = player1.move(player2.my_moves)
        p2_move = player2.move(player1.my_moves)
        player1_profit = player1.calculate_profit(p1_move, p2_move)
        player2_profit = player2.calculate_profit(p2_move, p1_move)
        player1.total_profit += player1_profit
        player2.total_profit += player2_profit
        player1.my_moves.append(p1_move)
        player2.my_moves.append(p2_move)
    return player1.total_profit, player2.total_profit

def round_robin_tournament(strategies, rounds):
    scores = {strategy: 0 for strategy in strategies}
    for i in range(len(strategies)):
        for j in range(i + 1, len(strategies)):
            strategy1 = strategies[i]
            strategy2 = strategies[j]
            profit1, profit2 = simulate_game(strategy1, strategy2, rounds)

            scores[strategy1] += profit1
            scores[strategy2] += profit2
    return scores

strategies = [
    'always_cooperate',
    'always_defect',
    'random',
    'tit_for_tat',
    'grim_trigger',
    'reverse_tit_for_tat',
    'pavlov',
    'defect_until_cooperate'
]

scores = round_robin_tournament(strategies, rounds=200)

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/74d5d70457b55da2f9aa4ec5c110f67b.png

克拉蒂卡的循环赛点数表。图片归作者所有

克拉蒂卡观察到采用“以牙还牙”策略的玩家平均获得了最大利润。这种策略比采用“始终背叛”策略的玩家多赚了 50 单位利润。因此,她决定在与伊希塔的对决中采用“以牙还牙”策略。

伊希塔进行了类似的模拟并得出相同的结论。两人都采取了“以牙还牙”的策略,在 200 天内获得了 600 单位的利润。如果采取“始终背叛”策略,他们只能获得 200 单位的利润。投资者们都很高兴。

好人先赢

政治学家罗伯特·阿克罗德创建了一个游戏,克拉蒂卡使用 30 多种策略在第一版中进行了 200 轮模拟。这个著名的比赛在 20 世纪 70 年代末和 80 年代初举行,旨在探索在迭代环境中的囚徒困境策略。他邀请了学者们提交计算机程序,这些程序将在游戏的重复轮次中竞争。其中一些人编写了包含大量代码的复杂算法。令人惊讶的是,简单的“以牙还牙”策略成为了最有效的策略。在克拉蒂卡的模拟中,“以牙还牙”是最佳策略。

游戏结束后,阿克罗德提出了他的策略,称为以牙还牙两次。在这个策略中,玩家开始游戏时合作,但如果对手连续两次背叛,玩家将背叛。他声称,如果有人采用这种策略参赛,它将赢得比赛。克拉蒂卡证实了这一点,是的,“以牙还牙两次”在她的模拟中获胜。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/e312439f85fbd37c0bc214bc163e3deb.png

克拉蒂卡的循环赛制与以牙还牙积分表。图片归作者所有

艾克斯罗德创建了一个具有不同规则的另一个锦标赛。参与者不知道他们会进行多少轮比赛。如果玩家知道他们会进行 200 轮比赛,有人声称他们不会在最后一轮合作(仅仅因为前 199 轮发生的事情并不重要)。类似的逻辑可以应用于第 199 轮,依此类推。这意味着我们又回到了所有人都会背叛的情况。这一次,许多人设计了复杂而聪明的策略来应对以牙还牙。这个游戏共有 62 个提交。令人惊讶的是,以牙还牙再次获胜。

人类行为

艾克斯罗德声称这些策略让我们对常见的人类行为有了很多了解。他基于人类行为,如善良、原谅、报复行为和清晰度,研究了这些策略,并发现了有趣的事实。

  1. 善良 – 善良的策略是以合作开始的策略。

  2. 原谅 – 在对手背叛后原谅对手的策略。

  3. 报复 – 在对手背叛后进行报复的策略。

  4. 清晰 – 容易理解的策略是清晰的策略。

下图展示了不同策略在克拉蒂卡锦标赛中的行为。"Y"和"N"的频率表示每种行为表现的程度。更多的字母意味着该行为更强烈地存在。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c4ec8a6d4898de70ba0615ce78230e85.png

不同策略的特性。图片归作者所有

  1. 好的策略优于坏的策略。随机策略将好策略和坏策略分开。**好人先得利!**要善良。

  2. 原谅策略往往表现更好。以牙还牙比以牙还牙更加宽容。同时,也不要太过宽容!

  3. 报复,但不要过于激进。不要像总是合作策略那样软弱,也不要像冷酷触发策略那样过于激进。以牙还牙策略对一次背叛表示原谅,但如果对手不改变行为,则会进行报复。

  4. 清晰地表达你的想法。尽管克拉蒂卡锦标赛中的所有策略都很清晰,但艾克斯罗德观察到,许多复杂的算法在他的锦标赛中未能击败简单的算法。

结论

艾克斯罗德对重复囚徒困境问题中超过 60 种策略的分析有类似的观察。他发现最成功的策略具有一些关键特征——它们是友好的(从合作开始),宽容的(但不过度),在被挑衅时愿意报复,并且方法明确。 这些特征有助于在重复互动中确保更好的结果。人们可以在策略和不同的现实生活场景之间建立联系,并从比赛中汲取经验教训。

本系列关于博弈论的第三部分文章将更多地关注“以牙还牙”策略。

博弈论,第三部分 – 你是你最常相处的前五个人平均水平的代表

感谢您阅读这篇文章!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值