博弈论中期大作业
闲话
这其实是个大作业来着……然而早上八点才开始写,中间还上了好几个小时的课(虽然全都没有听)(大嘘)。所幸最终还是学完了那本佶屈聱牙的英文教材的对应章节,并调包写完了代码,撸完了实验报告,DDL 刚过几分钟……认真完成作业对我来说实属不易,于是顺便贴到博客上来以供来者。下面是实验报告的内容。
实验原理
纯策略求解
直接枚举所有可能的纯策略,然后判断其是否是纳什均衡(是否所有玩家的策略都不劣于其最优反应函数)。时间复杂度和输入收益矩阵的大小相当。还可以进行一个简单优化:在枚举最后一个玩家策略时直接使用其最优反应函数作为其策略,因为选其他的肯定不是纳什均衡。
混合策略求解
对于两人混合策略博弈(也就是所谓双矩阵博弈),定义玩家 1 的收益矩阵为 \(\bold A\),策略为 \(\bold x\),玩家 2 的收益矩阵为 \(\bold B\),策略为 \(\bold y\)。
考虑如下多面体的顶点:
显然除了原点,所有的顶点都有至少一个博弈约束条件取到等号的。我们发现,取到等号的条件就是顶点对应的策略的对手最优反应函数可能的取值点。于是我们称此多面体为「最优反应」多面体。我们只需枚举不同多面体的所有顶点间的组合,判断它们是否是 MNE 即可。这个多面体的数学形式比较复杂,为了化简它,我们发现我们可以暂时不考虑概率归一化条件,转而归一化最优反应的约束条件,等到求解后再重新归一化,于是我们得到:
这个多面体的形式就很简单了,如同参考资料中所提到的,我们可以直接用 LFS 的主元替换方法求解。算法的复杂度是约 \(O(n2.6^n)\),具体复杂度及正确性证明详见参考资料,这里不多赘述。
实验实现
输入处理
综合运用 reshape
,transpose
等方法对输入的收益矩阵进行转置,得到便于处理的形式:
actions = [int(_) for _ in content[content.find('{')+1:content.find('}')].strip().split()]
payoff_mat = [int(_) for _ in content[content.find('}')+1:].strip().split()]
player_nr = len(actions)
payoff_mat = np.array(payoff_mat)
payoff_mat = payoff_mat.reshape(actions[::-1] + [player_nr])
payoff_mat = np.transpose(payoff_mat, ([_ for _ in range(player_nr + 1)][::-1]))
纯策略求解
直接搜索即可,另外对最后一个玩家进行了优化,上文已备述:
def search(strat, step, mat, f):
if step == player_nr:
if check(strat, mat):
print_strat(strat, f)
return
# small optimazation
if step == player_nr - 1:
strat[step] = brc(strat, step, mat)
search(strat, step + 1, mat, f)
return
for choice in range(actions[step]):
strat[step] = choice
search(strat, step + 1, mat, f)
混合策略求解
首先需要准备多面体的约束公式,并对存在负值的收益矩阵做平移处理:
# 平移
if np.min(payoff_mat[0]) <= 0:
payoff_mat[0] += -np.min(payoff_mat[0]) + 1
if np.min(payoff_mat[1]) <= 0:
payoff_mat[1] += -np.min(payoff_mat[1]) + 1
# B^T x - 1 <= 0
# -x <= 0
norm_1 = np.vstack((payoff_mat[1].T, -np.eye(actions[0])))
norm_1 = np.append(norm_1, np.array([-1 for _ in range(actions[1])] + [0 for _ in range(actions[0])]).reshape([actions[0] + actions[1], -1]), axis=1)
# Ay - 1 <= 0
# -y <= 0
norm_2 = np.vstack((payoff_mat[0], -np.eye(actions[1])))
norm_2 = np.append(norm_2, np.array([-1 for _ in range(actions[0])] + [0 for _ in range(actions[1])]).reshape([actions[0] + actions[1], -1]), axis=1)
然后运用 scipy.spatial.HalfspaceIntersection
求解出最优反应多面体的所有顶点:
inter_p1 = cal_inter(norm_1)[:-1]
inter_p2 = cal_inter(norm_2)[:-1]
hs1 = HalfspaceIntersection(norm_1, inter_p1)
hs2 = HalfspaceIntersection(norm_2, inter_p2)
最后对所有顶点进行归一化,去除零策略,判断是否为纳什均衡后输出即可:
for intersection1 in hs1.intersections:
# eliminate zero vectors
if np.fabs(np.sum(intersection1)) < 1e-6:
continue
# normalization
intersection1 = intersection1 / np.sum(intersection1)
for intersection2 in hs2.intersections:
# eliminate zero vectors
if np.fabs(np.sum(intersection2)) < 1e-6:
continue
# normalization
intersection2 = intersection2 / np.sum(intersection2)
# Check MNE
for intersection1 in hs1.intersections:
for intersection2 in hs2.intersections:
if check_MNE(payoff_mat, intersection1, intersection2):
print(f'Found\n{intersection1}\n{intersection2}')
总结反思
参考资料提到,对于混合策略求解,必须是非退化的博弈才可适用此凸包解法,否则顶点的枚举不能完全对应上所有的纳什均衡,这里没有再作过多考量和测试,算是本实验的尚待改进之处。
参考
A Pivoting Algorithm for Convex Hulls and
Vertex Enumeration of Arrangements and Polyhedra