简介:在多人非合作博弈分析中,纳什均衡的求解往往依赖收益矩阵的构建与优化计算。面向博弈论学习者及MATLAB开发者,这份压缩包提供了一套合作博弈下的均衡计算实现,从定义博弈矩阵、计算各玩家最佳响应,到判定满足条件的纳什均衡点,均配有可直接运行的m脚本。压缩包共6个文件,整体仅424KB,其中4个m脚本是核心算法代码,适合二次修改与复用;txt文档梳理了计算步骤与公式要点,pdf则补充了理论背景和MATLAB求解思路,帮助零基础读者理解代码背后的博弈逻辑。当前已有1119人学习下载,适合需要借助MATLAB快速验证博弈模型的研究者、学生或工程师。通过学习具体代码,可掌握博弈矩阵定义、最佳响应函数编写、均衡点搜索等关键环节,并能将参数替换到市场分析、网络竞争、政策制定等真实决策场景中,提升策略设计与行为预测能力。
1. 纳什均衡计算:从博弈矩阵到MATLAB求解的完整链路
很多人在拿到一份纳什均衡计算的MATLAB源码时,第一反应是去翻中间某个函数,然后把矩阵塞进去跑一遍。真正动手拆过才知道,难点从来不在求解器那一行,而在前面:收益矩阵怎么建才不违背博弈论假设、纯策略和混合策略该用哪条路径、多人博弈的结果怎么验证。尤其当你把纳什均衡从教科书案例搬到多智能体博弈场景时,矩阵维度和收敛判据的问题会立刻暴露出来。这套资源里包含了npg.m、gamer.m、simulationdata.m以及一份PDF说明,核心是用MATLAB完成纯策略穷举和混合策略迭代求解。适合想用代码验证博弈模型、又不想从零写线性互补算法的人。下面我会把求解链路拆开讲,从模型设定到参数调优,最后落到动态博弈和多智能体验证上。
2. 纳什均衡的理论前提与博弈建模:为什么不能直接套求解器
2.1 纯策略与混合策略的判定边界
纳什均衡的定义是:没有任何玩家能通过单方面改变策略来提高收益。但有一个经常被忽略的前提——这里的“策略”可以是纯策略,也可以是混合策略。纯策略均衡对应的是策略组合里的确定性选择,比如囚徒困境中双方都坦白;混合策略均衡则是玩家以一定概率随机选择多个纯策略,比如石头剪刀布中的均匀分布。
在MATLAB实现时,判定边界的意义在于选择不同的算法。纯策略均衡可以用best_response穷举得到,复杂度是策略组合数的乘积;混合策略均衡则需要求解概率分布,本质上是解一个非线性方程组或线性互补问题。如果只把纯策略的结果当作最终答案,遇到像“性别之战”这类只有混合策略均衡的博弈就会直接漏解。所以先要在代码里区分pure_strategy和mixed_strategy两条分支,而不是统一走迭代。
2.2 收益矩阵的标准化与占优策略剔除
常见的坑是收益矩阵没有标准化。比如双人博弈中,玩家1的收益矩阵和玩家2的收益矩阵维度不一致,或者收益值量纲差异过大,导致迭代求解时收敛到局部极值。我一般会在建模前做两步处理:
- 将收益矩阵统一转换为
n×m×p的三维数组,第一维是玩家序号,后两维是策略组合。 - 剔除严格劣策略,即某一策略在所有对手策略下的收益都不大于另一个策略,并且至少存在一个严格小于的情况。
下表是一个标准化的双人收益矩阵示例,行是玩家1的策略,列是玩家2的策略,每个单元格写成(收益1, 收益2):
| 策略 | L | R |
|---|---|---|
| U | (3, 2) | (0, 0) |
| D | (0, 0) | (2, 3) |
这个例子中不存在被严格占优的策略,所以不能简化,必须完整求解。而如果某一行每个单元格的收益都比另一行低,就可以直接删掉,降低求解规模。
2.3 求解纳什均衡的数学工具:线性互补与Lemke-Howson算法
混合策略纳什均衡的求解,在数学上等价于一个线性互补问题。对于双人博弈,Lemke-Howson算法是最经典的路径算法;对于多人博弈,通常转化为多线性函数求解或使用迭代逼近。MATLAB中没有内置的nash函数,所以资源里的npg.m实际上是自己实现的迭代求解器。
npg.m的典型思路是:初始化每个玩家的混合策略为均匀分布,然后循环计算每个玩家的最佳响应,再用一个学习率来更新当前策略,直到策略变化量小于阈值。这本质上是复制者动态的离散版本。要注意的是,这类迭代方法只能保证收敛到某个纳什均衡,不保证找到所有均衡,所以需要配合多个随机初始点来增加覆盖率。理论部分到这里就够了,下面直接看代码怎么落地。
3. MATLAB实现纳什均衡计算的源码拆解:从npg.m到仿真循环
3.1 博弈矩阵的定义与输入格式
源码包里的npg.m和gamer.m对矩阵格式有明确约定。最常见的输入是payoff{1}和payoff{2}两个元胞数组,分别存放玩家1和玩家2的收益矩阵。玩家1的收益矩阵每个元素对应行策略和列策略组合下的收益,玩家2的收益矩阵维度与玩家1一致。
下面是一个标准的定义格式:
% 双人博弈收益矩阵定义 % 玩家1: 行策略, 玩家2: 列策略 P1 = [3 0; 0 2]; % 玩家1的收益 P2 = [2 0; 0 3]; % 玩家2的收益 payoff = {P1, P2}; n_actions = size(P1, 1); % 每个玩家的策略数这段代码里,P1和P2的维度必须一致,否则后续npg.m在计算联合收益时会出现索引越界。n_actions在这里是2,说明每个玩家有两个纯策略。资源里的Untitled2.m通常会封装一个函数,接收payoff和迭代参数,返回混合策略概率向量。
3.2 最佳响应函数与纯策略穷举
对于纯策略纳什均衡,先实现一个最佳响应函数,逐行扫描每个玩家的最优策略。
function br = best_response(payoff, player, opponent_strategy) % 计算给定对手策略下的最佳纯策略 % payoff: 元胞数组, player: 玩家编号, opponent_strategy: 对手策略索引 n = size(payoff{player}, 1); if player == 1 rewards = payoff{player}(:, opponent_strategy); else rewards = payoff{player}(opponent_strategy, :)'; end [~, br] = max(rewards); end这里的参数opponent_strategy是假设对手已选定的策略索引。max函数返回收益最大的策略,如果有多个相同最大值,br会取第一个索引,这在纯策略判定时可能漏掉多个均衡。改进办法是使用find(rewards == max(rewards))返回所有最优策略,再对所有玩家做笛卡尔积,筛选出互为最佳响应的组合。gamer.m里通常就是做了这层遍历。
3.3 混合策略求解:npg.m的核心逻辑
npg.m是整套源码的核心,它采用迭代法逼近混合策略纳什均衡。一段简化的核心循环如下:
function [mixed, history] = npg(payoff, alpha, tol, max_iter) % 迭代求解双人混合策略纳什均衡 % alpha: 学习率, tol: 收敛阈值, max_iter: 最大迭代次数 n1 = size(payoff{1}, 1); n2 = size(payoff{2}, 1); mixed = ones(1, n1 + n2) / (n1 + n2); % 初始化均匀混合策略 history = zeros(max_iter, n1 + n2); for k = 1:max_iter % 拆分玩家1和玩家2的概率分布 p = mixed(1:n1); q = mixed(n1+1:end); % 计算玩家1的期望收益向量及其最佳响应 exp1 = payoff{1} * q; % 每个纯策略的期望收益 [~, br1] = max(exp1); % 计算玩家2的期望收益向量 exp2 = payoff{2}' * p; [~, br2] = max(exp2); % 更新策略:向最佳响应方向加权移动 new_p = p; new_p(br1) = new_p(br1) + alpha; new_p = new_p / sum(new_p); new_q = q; new_q(br2) = new_q(br2) + alpha; new_q = new_q / sum(new_q); mixed = [new_p, new_q]; history(k, :) = mixed; % 收敛判断:策略变化量小于阈值 if norm([new_p - p, new_q - q]) < tol break; end end history = history(1:min(k, max_iter), :); end这段代码的参数含义:alpha是学习率,通常设为0.1到0.5之间,太大会导致震荡,太小则收敛慢;tol是策略变化量的欧几里得范数阈值,一般取1e-6;max_iter是上限,防止不收敛时死循环。这里更新方式是最简单的最佳响应增量,严格来说叫“虚构游戏”方法,不是标准复制者动态,但原始资源里就是这么处理的。实际使用时,我建议把alpha改成衰减形式,例如alpha = alpha0 / sqrt(k),能改善后期的收敛稳定性。
3.4 合作博弈与联盟收益分配:Shapley值计算
标题里的“MATLAB合作博弈”通常指向Shapley值的计算。合作博弈关注的是联盟能产生多少总收益以及如何公平分配。simulationdata.m里可能存放了每个联盟的收益数据集,计算Shapley值的公式是:
function sv = shapley_value(v, n) % v: 联盟收益函数, 输入为联盟成员索引向量 % n: 玩家总数 sv = zeros(1, n); for i = 1:n for S = 1:2^n - 1 members = find(bitget(S, 1:n)); % 枚举所有不含i的联盟 if any(members == i), continue; end union_idx = [members, i]; v_S = v(members); v_Si = v(union_idx); weight = factorial(length(members)) * factorial(n - length(members) - 1) / factorial(n); sv(i) = sv(i) + weight * (v_Si - v_S); end end end注意这里枚举联盟用的是整数S的二进制位,时间复杂度是O(2^n),玩家数量超过15就不适用了。simulationdata.m中如果预先存储了所有2^n个联盟的收益,可以直接查表。Shapley值的核心参数是v这个联盟收益函数,它必须满足超可加性,即两个不相交联盟合并后的收益不小于单独收益之和,否则分配结果会出现负边际贡献。
4. 实战:双人零和与多人博弈的纳什均衡计算与参数调优
4.1 双人零和博弈:线性规划求解minimax
双人零和博弈可以用线性规划直接求出唯一均衡,这也是验证npg.m迭代结果是否正确的最好办法。给定玩家1的收益矩阵A(玩家2的收益为-A),求解minimax可以转化为下面这个线性规划:
% 使用linprog求解零和博弈的纳什均衡 f = [zeros(n, 1); 1]; % 目标: 最大化v, 等价于最小化 -v A_ineq = [-A, ones(n, 1)]; % 约束: A*p >= v b_ineq = zeros(n, 1); Aeq = [ones(1, n), 0]; % sum(p) = 1 beq = 1; lb = [zeros(n, 1); -inf]; ub = [ones(n, 1); inf]; options = optimoptions('linprog', 'Display', 'off'); x = linprog(f, A_ineq, b_ineq, Aeq, beq, lb, ub, options); p = x(1:n); % 玩家1的最优混合策略这段代码的关键是构造约束:A_ineq * [p; v] >= 0等价于每个纯策略的期望收益至少为v。f的最后一个元素是1,对应最小化-v,因为linprog默认求最小值。当npg.m迭代得到的结果与这里的p的差小于1e-4时,说明迭代器实现正确。
4.2 三人博弈的迭代逼近与收敛判据
三人博弈的收益矩阵是三维数组,npg.m的原始版本只支持双人,需要扩展。扩展后的核心循环是嵌套计算每个玩家的最佳响应:
function mixed3 = nash_3p(payoff3, alpha, tol) % payoff3: 3xPxQxR的元胞矩阵, 每个元素是三维收益 % 初始化均匀概率 p = ones(3,1)/3; q = ones(3,1)/3; r = ones(3,1)/3; for iter = 1:10000 % 玩家1的期望收益: 遍历自己的策略, 另两个预期概率固定 exp1 = zeros(3,1); for a = 1:3 exp1(a) = sum(payoff3{1}(a,:,:) .* reshape(q * r', 1, [])); end [~, br1] = max(exp1); % 玩家2和玩家3类似... % 更新 p, q, r, 并检查三者的变化 delta = norm([p - p_old, q - q_old, r - r_old]); if delta < tol, break; end end mixed3 = [p; q; r];这里reshape(q * r', 1, [])把策略组合的联合概率拉成向量,然后与收益子矩阵做逐元素相乘。收敛判据必须同时覆盖三个玩家的概率变化,不能只看某一个。实际中三人博弈迭代经常出现循环振荡,我一般会加上平滑项,即new_p = (1-alpha)*old_p + alpha*br_p,并且alpha从0.5开始按迭代次数衰减。
4.3 参数设置与常见误用:学习率、收敛阈值与初始点
参数调优的目标不是让迭代跑得最快,而是让结果稳定可复现。下表列出常见的参数选择及影响:
| 参数 | 取值范围 | 影响与风险 |
|---|---|---|
alpha学习率 | 0.01 ~ 0.5 | 过大导致策略概率在最佳响应之间跳变,不收敛 |
tol收敛阈值 | 1e-8 ~ 1e-4 | 太严格导致迭代次数指数上升,太宽松得到伪均衡 |
| 初始策略分布 | 均匀分布或随机 | 不同初始点可能收敛到不同均衡,推荐多组随机起点 |
max_iter | 1000 ~ 100000 | 对大型博弈需调高,同时监控每步的norm(delta) |
一个常见误用是把博弈矩阵写成收益差而非绝对收益。纳什均衡只关心收益的顺序,不关心差值大小,但迭代算法中梯度幅度受影响,所以绝对收益更稳妥。另一个误用是零和博弈中直接把两个收益矩阵相加为0,却忘了把玩家2的收益取反。正确写法是payoff{2} = -payoff{1},这样npg.m才能处理。
5. 把MATLAB代码用于多智能体博弈的验证与进阶
5.1 用仿真数据验证均衡稳定性
资源里的simulationdata.m不是直接给出纳什均衡结果,而是生成仿真数据来验证均衡的稳定性。常见做法是:在均衡策略附近加入随机扰动,观察系统是否回到原均衡。我给一个简单的稳定性测试:
function stable = check_stability(payoff, eq_strategy, epsilon, sims) % eq_strategy: 混合策略向量, epsilon: 扰动幅度, sims: 仿真次数 stable = 0; n = length(eq_strategy); for k = 1:sims perturbed = eq_strategy + epsilon * randn(1, n); perturbed = max(perturbed, 0); perturbed = perturbed / sum(perturbed); % 重新归一化 % 用npg从perturbed开始迭代, 看是否收敛回eq_strategy [out, ~] = npg(payoff, 0.1, 1e-6, 500); if norm(out - eq_strategy) < 0.01 stable = stable + 1; end end stable = stable / sims; end这里的epsilon一般取0.01到0.1,太大等于重新随机初始化,测试没有意义。如果稳定率大于0.95,可以认为该均衡在演化意义下是稳定的。注意这个测试只适用于对称或接近对称的博弈,对于多均衡的博弈,微扰后可能收敛到另一个合法均衡,这不代表原均衡错误,而是说明系统存在多个吸引域。
5.2 从静态均衡到动态博弈的拓展
npg.m处理的是单阶段博弈。多智能体博弈中,智能体之间往往存在多轮交互,你需要把静态均衡计算嵌入到每个回合的策略更新里。一个轻量级做法是:每轮根据当前全局状态计算本轮收益矩阵,然后调用npg得到混合策略,再按该策略采样动作。由于状态变化,收益矩阵也在变,所以每轮必须重新构建矩阵,不能复用上一轮的结果。这会造成性能瓶颈,因为npg本身是迭代算法,实时性要求高的场景可用查表替换:预先对离散状态空间做网格化,离线计算好均衡策略表,运行时直接索引。
5.3 调试技巧:矩阵维度不匹配与收益异常值
遇到最多的问题是维度不匹配。payoff{1}和payoff{2}的行列数明明一致,但进入npg.m后报错索引超界。检查点有两个:一是是否使用了元胞数组而不是三维数值数组,二是策略数是否从0开始计数。MATLAB索引从1开始,如果某处写成了for i = 0:n-1就会出错。另一类问题是收益矩阵中包含Inf或NaN,这通常来自收益计算时的不收敛除零,建议用isfinite过滤一遍。调试时把history矩阵的最后几行打出来,如果概率一直不变化但delta始终不降,说明alpha太大或博弈本身存在极限环。
收益异常值还可能是数据读取时顺序错误。npg.m假设的格式是玩家1的收益矩阵中行对应玩家1策略、列对应玩家2策略,如果从Excel或CSV读入时行列转置,最佳响应方向就反了。验证办法是构造一个已知均衡的小矩阵,比如石头剪刀布,运行npg输出概率应接近1/3。每次写新的博弈模型前先跑一遍这个自检程序,能省下大量排查时间。
本文还有配套的精品资源,点击获取