零和博弈指参与博弈的双方收益总和为零,一方的收益恰好是另一方的损失,这类场景在竞价、对抗决策等领域十分常见。纳什均衡是博弈中的稳定状态,在零和博弈中可以通过线性规划的方法求解,SciPy的optimize模块提供了线性规划求解能力,能够支撑这类计算需求。

零和博弈的基本建模逻辑
假设零和博弈有两个参与者,行参与者有m个策略,列参与者有n个策略,收益矩阵A是m行n列的矩阵,其中A[i][j]表示行参与者选择第i个策略、列参与者选择第j个策略时行参与者的收益。由于是零和博弈,列参与者的收益就是-A[i][j]。
行参与者的目标是最大化自己的最小可能收益,也就是求解混合策略概率向量x(x长度m,元素非负且和为1),使得min_{j}(A x)_j最大化。列参与者的目标是最小化自己的最大可能损失,也就是求解混合策略概率向量y(y长度n,元素非负且和为1),使得max_{i}(A^T y)_i最小化。
根据博弈论中的极小极大定理,这两个优化问题的解是相等的,对应的目标值就是博弈的值v。
基于SciPy的线性规划模型构建
我们将行参与者的优化问题转化为线性规划的标准形式。引入变量v表示行参与者的最小收益下界,优化目标为最大化v,约束条件为:
- 对于所有j,sum_{i} A[i][j] * x[i] >= v
- sum_{i} x[i] = 1
- x[i] >= 0,v无符号限制
为了适配SciPy的linprog接口,我们需要将问题转化为最小化目标,同时处理变量的符号。令目标为最小化 -v,变量向量为[x1, x2, ..., xm, v],则目标函数系数为[0, 0, ..., 0, -1]。
约束条件改写为:
- 对于所有j,sum_{i} A[i][j] * x[i] - v >= 0,即 -sum_{i} A[i][j] * x[i] + v <= 0
- sum_{i} x[i] = 1,即 sum_{i} x[i] + 0*v = 1
- x[i] >= 0,v无符号限制,因此v不需要非负约束
完整代码实现
以下是使用SciPy求解零和博弈纳什均衡的完整代码,包含收益矩阵定义、线性规划模型构建、求解和结果解析的完整流程:
import numpy as np
from scipy.optimize import linprog
def solve_zero_sum_nash(A):
"""
求解零和博弈的纳什均衡
:param A: 行参与者的收益矩阵,形状为(m, n)的二维数组
:return: 行参与者混合策略,列参与者混合策略,博弈值
"""
m, n = A.shape
# 构建行参与者的线性规划问题:最大化v,约束A x >= v, sum(x)=1, x>=0
# 变量顺序:[x1, x2, ..., xm, v],目标最小化 -v
c = np.zeros(m + 1)
c[-1] = -1 # 目标函数系数,对应-v的最小化
# 不等式约束:-A[i][j] * x[i] + v <= 0,共n个约束
A_ub = np.zeros((n, m + 1))
for j in range(n):
A_ub[j, :m] = -A[:, j] # x的系数
A_ub[j, -1] = 1 # v的系数
b_ub = np.zeros(n)
# 等式约束:sum(x) = 1
A_eq = np.zeros((1, m + 1))
A_eq[0, :m] = 1
b_eq = np.array([1])
# 变量边界:x >= 0,v无限制
bounds = [(0, None)] * m + [(None, None)]
# 求解线性规划
res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method="highs")
if not res.success:
raise ValueError("行参与者线性规划求解失败:" + res.message)
x = res.x[:m] # 行参与者混合策略
v = res.x[-1] # 博弈值
# 求解列参与者的线性规划问题:最小化v,约束A^T y <= v, sum(y)=1, y>=0
# 变量顺序:[y1, y2, ..., yn, v],目标最小化v
c_col = np.zeros(n + 1)
c_col[-1] = 1
# 不等式约束:A[i][j] * y[j] - v <= 0,共m个约束
A_ub_col = np.zeros((m, n + 1))
for i in range(m):
A_ub_col[i, :n] = A[i, :] # y的系数
A_ub_col[i, -1] = -1 # v的系数
b_ub_col = np.zeros(m)
# 等式约束:sum(y) = 1
A_eq_col = np.zeros((1, n + 1))
A_eq_col[0, :n] = 1
b_eq_col = np.array([1])
# 变量边界:y >= 0,v无限制
bounds_col = [(0, None)] * n + [(None, None)]
res_col = linprog(c_col, A_ub=A_ub_col, b_ub=b_ub_col, A_eq=A_eq_col, b_eq=b_eq_col, bounds=bounds_col, method="highs")
if not res_col.success:
raise ValueError("列参与者线性规划求解失败:" + res_col.message)
y = res_col.x[:n] # 列参与者混合策略
return x, y, v
# 测试用例:收益矩阵示例
if __name__ == "__main__":
# 定义收益矩阵,行参与者两个策略,列参与者两个策略
A = np.array([
[3, 1],
[0, 2]
], dtype=float)
try:
x, y, v = solve_zero_sum_nash(A)
print("行参与者混合策略:", np.round(x, 4))
print("列参与者混合策略:", np.round(y, 4))
print("博弈值:", np.round(v, 4))
except ValueError as e:
print(e)
常见错误与规避方法
收益矩阵方向混淆
很多开发者会混淆收益矩阵的归属,错误地将列参与者的收益放入矩阵。需要明确的是,我们定义的A必须是行参与者的收益,否则求解结果会完全相反。如果拿到的是列参与者的收益矩阵B,需要先取负数得到A = -B再进行求解。
线性规划约束写反
在转化约束条件时,容易把不等式的方向写反。需要牢记行参与者的约束是A x >= v,转化为标准线性规划形式时要正确调整系数符号,建议每次建模后先手动验证小尺寸矩阵的约束是否正确。
变量边界设置错误
博弈值v是没有非负限制的,如果错误给v加上非负约束,会导致求解结果不符合零和博弈的理论结果,尤其是当博弈值为负的时候会出现错误。
结果验证
对于上面的测试用例,收益矩阵为[[3,1],[0,2]],手动计算可知行参与者的最优混合策略是[2/3, 1/3],列参与者的最优混合策略是[1/3, 2/3],博弈值为4/3≈1.3333,和代码运行结果一致,说明建模和求解逻辑是正确的。
SciPy零和博弈纳什均衡线性规划game_theory修改时间:2026-07-11 09:36:14