传统的战略式博弈求解方法——虚拟博弈(Fictitious Play)讲解及其代码
虚拟博弈是博弈论中一种传统的方法,其历史真的非常久远,于1951年被Brown, George W 提出。
其核心思想非常简单,就是利用博弈论中常用的反应函数思想。使每个智能体拥有两个策略集。一个是最优策略集,一个是历史平均策略集。在每一轮博弈的开始,每个均智能体根据对手的历史平均策略集,找到一个最优的针对策略。然后根据历史平均策略和本轮最优策略更新自己的历史平均策略。
拿石头剪刀布举例子:首先第一轮随机出拳,如果P1石头,P2剪刀。在第二轮时,P1根据P2的历史数据(P2只出了剪刀)得出自己应该出石头,则P1还是出石头,P2根据P1历史数据(P1只出了石头)得出自己应该出布。所以第二轮P1石头,P2布;玩家更新自己的历史策略集P1还是只出了石头,P2有50%的情况出了剪刀,50%的情况出布,以此类推。。。随着迭代的继续,策略会慢慢收敛到纳什均衡。
但是,虚拟博弈并不能解决所有问题。虚拟博弈只能解决零和博弈,或者是包含纯策略纳什均衡解的常和博弈。在常和博弈中如果存在多个纳什均衡,则虚拟博弈收敛的结果会由收益矩阵和初始策略有关,并不能保证收敛到“最优均衡”。此外,虚拟博弈依赖于问题是战略式描述,如果要求扩展式的问题,则一直等到2015年FSP (Fictitious Self-Play) 虚拟自我博弈出现才能处理,我们之后再说。
附虚拟博弈代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | # -*- encoding:utf-8 -*- """ @Copyright: Copyright (c) 2020 @Author: JuQi @E-mail: [email protected] @Title: fp @Created on: 2020/11/17 0:26 @Abstract: """ import numpy as np class Player(object): def __init__(self, policy_len, utility, num): """ :param policy_len: 策略个数 :param utility: 收益矩阵 :param num: 玩家0写0 玩家1写1 """ self.utility = utility self.policy_len = policy_len self.policy = np.random.random(self.policy_len) self.history = np.zeros(self.policy_len) self.num = num def change_policy(self, op_pro): """ 根据传入的对手历史策略,选择自己的最优策略,并改变自己的策略 :param op_pro: 对手策略 """ earn = op_pro * self.utility money_sum = np.sum(earn, axis=1 - self.num) best_choice = np.argmax(money_sum) self.history[best_choice] += 1 self.policy = self.history / np.sum(self.history) def get_policy(self): """ :return: 返回自己本轮策略 """ if self.num == 0: return np.reshape(self.policy, (self.policy_len, 1)) else: return self.policy def exploitability(self, op_pro): """ 测试对手策略的可利用度(实质就是epsilon-纳什均衡的epsilon) :param op_pro: 对手策略 """ earn = op_pro * self.utility money_sum = np.sum(earn, axis=1 - self.num) best_choice = np.argmax(money_sum) print('p' + str(1 - self.num) + ' exploitability:', money_sum[best_choice]) class Nash(object): def __init__(self, p0, p1): self.p0 = p0 self.p1 = p1 def get_nash_equilibrium(self, loop_time): """ 求解纳什均衡 :param loop_time: 迭代次数 """ for i in range(loop_time): self.p0.change_policy(self.p1.get_policy()) self.p1.change_policy(self.p0.get_policy()) def show_result(self): """ 显示结果 """ print('p0', self.p0.get_policy()) print('p1', self.p1.get_policy()) def show_exploitability(self): """ 显示可利用度 """ p0.exploitability(self.p1.get_policy()) p1.exploitability(self.p0.get_policy()) if __name__ == '__main__': """ # 以下例子求解如下纳什均衡(囚徒困境) # P0╲P1 坦白 抵赖 # 坦白 -4,-4 0,-5 # 抵赖 -5, 0 -1,-1 u0 = np.array( [[-4, 0], [-5, -1]] ) u1 = np.array( [[-4, -5], [0, -1]] ) p0 = Player(2, u0, 0) p1 = Player(2, u1, 1) """ # 以下例子求解如下纳什均衡(石头剪刀布) # P0╲P1 石头 剪刀 布 # 石头 0, 0 1,-1 -1, 1 # 剪刀 -1, 1 0, 0 1,-1 # 布 1,-1 -1, 1 0, 0 u0 = np.array( [[0, 1, -1], [-1, 0, 1], [1, -1, 0]] ) u1 = np.array( [[0, -1, 1], [1, 0, -1], [-1, 1, 0]] ) p0 = Player(3, u0, 0) p1 = Player(3, u1, 1) nash = Nash(p0, p1) nash.get_nash_equilibrium(1000) nash.show_result() nash.show_exploitability() |