ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

纳什均衡计算全解析:MATLAB支持枚举与线性规划求解双矩阵博弈

纳什均衡计算全解析:MATLAB支持枚举与线性规划求解双矩阵博弈 简介纳什均衡计算与MATLAB实现是博弈论学习和应用中的常见难点这份资料包恰好提供了从理论到代码的系统参考。资源共6个文件包括4个.m源码文件、1个txt计算说明和1个pdf理论文档整体仅424KB结构紧凑可快速定位所需内容。源码覆盖博弈矩阵构建、纯策略与混合策略均衡求解并通过优化函数或自定义迭代完成计算txt文件给出公式推导与关键步骤pdf则补充理论背景、实例分析和可能的应用方向。对于纯策略均衡不存在的情形资料也涉及概率分布下的混合策略求解思路能帮助读者理解期望收益最大化与不动点迭代的核心逻辑。已有817人学习使用适合经济学、管理学或社会科学方向的学生完成课程作业也可供研究人员在复杂系统决策分析中快速搭建纳什均衡计算工具。1. 双矩阵博弈的纳什均衡公式与 MATLAB 计算路径经常遇到这种咨询教材上纳什均衡定义很简洁真要把 A、B 两个支付矩阵丢进 MATLAB 算出均衡策略反而不知道从哪下手。原因在于混合策略均衡不是函数极值点而是满足“支撑条件”的不动点行玩家把正概率放在最优反应集内列玩家同理。纯策略均衡可以逐点比较枚举混合策略则需要解一组带支撑集约束的方程组。接下来从定义公式出发给出支持枚举算法和零和博弈线性规划两套 MATLAB 源码并说明数值容差、退化博弈和验证技巧帮你在这个经典问题上彻底摆脱“调包不调参”的状态。2. 支持枚举算法从支撑条件到 MATLAB 函数实现2.1 混合策略均衡的支撑条件双矩阵博弈中行玩家支付矩阵 A 为 m×n列玩家矩阵 B 同样为 m×n。混合策略用概率向量 x 和 y 表示期望支付为 xᵀAy 与 xᵀBy。纳什均衡的不等式定义对计算不友好工程实现时更常用的是支撑条件support condition对行玩家若 xᵢ 0则 (Ay)ᵢ maxₖ(Ay)ₖ对列玩家若 yⱼ 0则 (Bᵀx)ⱼ maxₗ(Bᵀx)ₗ。支撑条件的意义很简单均衡策略没有理由把正概率放在收益更低的纯策略上。反过来只要 x、y 满足上述条件就一定构成纳什均衡。枚举法就建立在“如果均衡存在其支撑集必然是某个 (S,T)”这一观察上。给定候选支撑集 S、T设 k|S|、l|T|则均衡策略和均衡支付 v、u 满足A(S,T)·y_T v·1_kB(S,T)ᵀ·x_S u·1_l∑x_S 1∑y_T 1这是一个 kl2 元线性方程组。v 和 u 是未知标量不是预先给定的值。把等式和归一化条件全部拼进系数矩阵 M 即可求解。2.2 求解函数nash_from_support.mfunction [x, y, v, u, ok] nash_from_support(A, B, S, T, tol) % 给定候选支撑集 S、T解线性方程组得到混合策略均衡候选 % 输入 A: m×n 行玩家支付矩阵B: m×n 列玩家支付矩阵 % 输入 S、T: 支撑集索引tol: 数值容差 if nargin 5, tol 1e-8; end k numel(S); l numel(T); M zeros(kl2); % 系数矩阵 rhs zeros(kl2, 1); % 右端项 % 行玩家支撑条件: A(S,T)*y_T - v 0 M(1:k, k1:kl) A(S,T); M(1:k, kl1) -1; % 列玩家支撑条件: B(S,T)*x_S - u 0 M(k1:kl, 1:k) B(S,T); M(k1:kl, kl2) -1; % 概率归一化 M(kl1, 1:k) 1; rhs(kl1) 1; M(kl2, k1:kl) 1; rhs(kl2) 1; z lsqminnorm(M, rhs); % 退化时也不直接报错 if norm(M*z - rhs) 1e-6 % 残差过大说明该支撑集无解 ok false; x []; y []; v []; u []; return; end xS z(1:k); yT z(k1:kl); v z(kl1); u z(kl2); if any(xS -tol) || any(yT -tol) % 概率不允许负数 ok false; return; end x zeros(size(A,1),1); y zeros(size(A,2),1); x(S) max(xS, 0); y(T) max(yT, 0); % 支撑集外策略收益不得高于均衡支付 if max(A*y - v) tol || max(B*x - u) tol ok false; return; end ok true; end这段代码的要点是M 矩阵前 k 行把“行玩家支撑集内每个纯策略的期望收益相等且等于 v”写成等式接着 l 行对应列玩家最后两行是概率归一化。用 lsqminnorm 而不是反斜杠运算符是因为退化博弈中 M 可能奇异lsqminnorm 会返回最小范数解配合残差检查比直接报错更实用。支撑集外检查用max(...) tol而不是any(abs(...) tol)因为定义只要求“不高于”允许出现小于 v 的偏差。2.3 主循环nash_support_all.mfunction eqs nash_support_all(A, B, tol) % 遍历全部非空支撑集返回所有纳什均衡 if nargin 3, tol 1e-8; end [m, n] size(A); eqs struct(x, {}, y, {}, v, {}, u, {}); for sm 1:2^m-1 for tn 1:2^n-1 S find(bitget(sm, 1:m)); % 行支撑集 T find(bitget(tn, 1:n)); % 列支撑集 [x, y, v, u, ok] nash_from_support(A, B, S, T, tol); if ok eqs(end1) struct(x, x, y, y, v, v, u, u); end end end eqs dedup_eqs(eqs, tol); end主循环用 m、n 位的 bitmask 枚举所有非空子集bitget(sm, 1:m)把整数拆成逻辑向量再find取索引。这种做法比 nchoosek 循环快而且代码直观。对 3×3 的课程设计题目两层循环至多 7×7 次毫秒级出结果m6、n6 时约 63×633969 次线性求解也就是秒级。去重部分用两两比较function eqs dedup_eqs(eqs, tol) % 按策略向量距离合并重复均衡 keep true(1, numel(eqs)); for i 1:numel(eqs) for j i1:numel(eqs) if norm(eqs(i).x - eqs(j).x) sqrt(tol) ... norm(eqs(i).y - eqs(j).y) sqrt(tol) keep(j) false; end end end eqs eqs(keep); end去重阈值取 sqrt(tol) 而不是 tol是因为迭代求解的误差在策略分量上会放大一倍量级。实际使用中如果发现同一均衡被返回两次优先调整这个阈值而不是全局放宽 tol。算例用石头剪刀布A [0 1 -1; -1 0 1; 1 -1 0]B -A。调用nash_support_all(A, B, 1e-10)后唯一均衡为 x y (1/3, 1/3, 1/3)。同时枚举法也会把纯策略均衡检索出来比如把 A 换成囚徒困境的支付矩阵 A [-1 -3; 0 -2]B [-1 0; -3 -2]返回的 eqs 里会有一个支撑集大小为 1 的均衡对应双方都选背叛。一个函数同时覆盖两种均衡这是支持枚举相对其他局部搜索算法最明显的优势。3. 零和博弈线性规划linprog 求解 maxmin 策略3.1 零和博弈与线性规划约束当 B -A 时博弈是零和的。行玩家的收益最大化问题可以写成 maxmin 形式选择 x 使得最坏情况下的期望收益尽量大。设价值为 v则约束是行玩家所有纯策略的期望收益不低于 v加上概率归一化max vs.t. Aᵀx ≥ v∑xᵢ 1x ≥ 0约束 Aᵀx ≥ v 不是 linprog 接受的标准形式需要改写成 -Aᵀx v ≤ 0。列玩家的问题对称min us.t. Ay ≤ u∑yⱼ 1y ≥ 0。两个问题分别求解后理论上 v 与 u 相等就是博弈的价值。linprog 参数行玩家取值含义f[zeros(m,1); -1]最小化 -v 等价于最大化 vA_ineq, b_ineq[-A, ones(n,1)], zeros(n,1)每个纯策略收益 ≥ vAeq, beq[ones(1,m), 0], 1概率之和为 1lb[zeros(m,1); -inf]x 非负v 无下界注意 A_ineq 的行数是列玩家的策略数 n列数是 m1。把 -A 的负号漏掉是最常见的符号错误。3.2 完整求解函数 zero_sum_nash.mfunction [x, y, v] zero_sum_nash(A) % 零和博弈纳什均衡行玩家 maxmin列玩家 minmax % 返回行策略 x、列策略 y、博弈价值 v [m, n] size(A); opts optimoptions(linprog, Display, off, Algorithm, dual-simplex); % 行玩家max v, 约束 -A*x v 0 f1 [zeros(m,1); -1]; A1 [-A, ones(n,1)]; b1 zeros(n,1); Aeq1 [ones(1,m), 0]; beq1 1; lb1 [zeros(m,1); -inf]; sol1 linprog(f1, A1, b1, Aeq1, beq1, lb1, [], opts); x sol1(1:m); v sol1(m1); % 列玩家min u, 约束 A*y - u 0 f2 [zeros(n,1); 1]; A2 [A, -ones(m,1)]; b2 zeros(m,1); Aeq2 [ones(1,n), 0]; beq2 1; lb2 [zeros(n,1); -inf]; sol2 linprog(f2, A2, b2, Aeq2, beq2, lb2, [], opts); y sol2(1:n); endAlgorithm选择 dual-simplex 而不是默认的 interior-point是因为支付矩阵经常退化dual-simplex 在退化顶点上的稳定性更好而且对中小规模问题没有性能劣势。如果只想求行玩家策略可以不写列玩家那一段但两个一起求能直接验证 v 是否等于列玩家的 u作为正确性检查。零和博弈有限纯策略一定有解所以这里没有对 exitflag 做判断如果你把 A 换成非常大的稀疏矩阵建议补一段 exitflag 检查把求解失败信息抛出来。3.3 与支持枚举法的一致性验证把支持枚举法返回的均衡代入零和函数对比可以同时排查两边代码的符号错误G randn(5,5); G G - G; % 构造斜对称零和博弈 [x, y, v] zero_sum_nash(G); eqs nash_support_all(G, -G, 1e-8); x_enum eqs(1).x; y_enum eqs(1).y; disp([x, x_enum]); % 两列策略应一致 disp([v, x_enum * G * y_enum]); % 价值也应一致如果这里不一致优先检查 A1 和 A2 的维度。一个快速定位技巧临时把 lb 中 v 分量的下界改成 0看求解器是否报不可行报错位置通常能直接指出是哪个约束写反了。4. 退化博弈与数值容差让完整遍历不漏解4.1 奇异方程组为什么不能直接用反斜杠支持枚举法在理论上是完备的但工程实现有个明显的坎当候选支撑集对应退化博弈时M 矩阵奇异M \ rhs会直接报错。所谓退化是指在某个均衡处有超过支撑集大小的纯策略同为最优反应导致等号约束线性相关。典型例子是全部支付为 0 的矩阵任意策略组合都是均衡此时枚举任何 (S,T) 都会构造出奇异 M。处理办法是第 2 章代码里的 lsqminnorm 加残差检查。但残差阈值 1e-6 不是通用值它应该和支付矩阵的量级挂钩。常见做法是先归一化支付矩阵再做计算scale max(1, max(abs(A(:)))); A A / scale; B B / scale;归一化不改变均衡策略集合但让 v、u 落在 [-1, 1] 量级这时候固定用 1e-8 的容差即可。如果没有归一化就直接跑原始矩阵不同量级下会误报“无解”本质是容差设置不合理而不是算法错了。4.2 退化均衡的扰动检查完整枚举退化博弈时同一个均衡往往对应多个支撑集去重后依然可能残留数值噪声导致的假均衡。一般做法是用扰动法做第二轮验证对支付矩阵加一个幅度很小的随机噪声重新计算均衡再回代原矩阵验证 regret。如果扰动后均衡消失或突变说明原结果处在退化边界上应标记为可疑不要直接作为最终答案。rng(1); A_pert A 1e-7 * randn(size(A)); eqs_pert nash_support_all(A_pert, B, 1e-8); % 对每个扰动均衡回代原始矩阵验证 for i 1:numel(eqs_pert) eqs_pert(i).regret nash_regret(A, B, eqs_pert(i).x, eqs_pert(i).y); end扰动幅度 1e-7 与容差 1e-8 要配套扰动过大会把本来不存在的均衡“制造”出来扰动过小又不足以打破退化约束的奇异性。4.3 linprog 报错的三类情况零和博弈用 linprog 有三个高频报错。第一个是“未定义函数或变量 linprog”说明当前 MATLAB 没有 Optimization Toolbox用which linprog确认。没有工具箱时小规模零和博弈可以退回第 2 章的支持枚举法。第二个是许可证相关的提示比如许可证不允许使用 Optimization Toolbox此时ver能看到已装工具箱列表解决方式是申请包含该工具箱的许可证。第三个是求解终止但 v 明显不合理通常不是求解器问题而是 A1 少写了负号比如把[-A, ones(n,1)]写成了[A, ones(n,1)]约束方向完全反了。license(test, Optimization_Toolbox) % 返回 1 表示可用这条命令值得在调试前先跑一次直接把工具箱问题排除掉。4.4 中文注释乱码与脚本编码从压缩包解压出来的老代码经常带中文注释用新版本 MATLAB 打开后注释变成乱码。主要原因是旧脚本是 ANSI/GBK 编码MATLAB 2023a 及以后默认按 UTF-8 读取脚本编码不匹配才会乱码。这和算法本身无关但会干扰调试。做法是用任意文本编辑器把 .m 文件另存为 UTF-8再重新打开注意编辑器设置里不要把“自动猜测编码”关掉否则打开 GBK 存档时会按 UTF-8 硬读。这个坑不处理后面的代码逻辑再正确注释也帮不上忙。5. 均衡的 regret 验证与大规模博弈出路5.1 regret 检验函数数值方法返回的均衡必须做残差验证。均衡遗憾值regret定义为行玩家所有纯策略中的最高期望收益减去混合策略期望收益列玩家同样计算二者取最大。不超过容差才认为结果是数值意义上的均衡。function reg nash_regret(A, B, x, y) % 计算纳什均衡遗憾值小于 1e-6 可认为收敛 v x * A * y; reg_row max(A * y) - v; reg_col max(B * x) - x * B * y; reg max([reg_row, reg_col]); end对支持枚举法返回的 eqs 逐项跑这个函数任何一项超过 1e-6 就说明支撑集约束没满足优先排查 lsqminnorm 残差阈值是否偏大。regret 的另一个用法是对比两种算法同一个零和矩阵用枚举法和 linprog 各自求解谁的 regret 更小谁在当前矩阵上更可信。5.2 蒙特卡洛抽查regret 做的是顶点精确检查蒙特卡洛负责边界抽查。用 rand 生成 1000 个随机混合策略归一化到概率单纯形逐个检查行扰动策略的收益是否超过 v 1e-6列扰动同理。两者都通过均衡才适合写进报告或作为后续实验的基准。这套方法不依赖具体求解器任何算法输出的结果都能用同一套代码验收。5.3 规模判断与落盘支持枚举法复杂度是 O(2^(mn))mn 超过 16 建议直接换 Lemke-Howson 或复制者动态零和博弈则继续用 linprog没有这个规模瓶颈。判断标准2^m * 2^n超过 1e6 就不要再跑 nash_support_all。所有均衡和对应 regret 值用writematrix或save落盘课程设计报告引用数值时直接附上验证结果比单独贴代码更有说服力。本文还有配套的精品资源点击获取
返回列表