多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

三参与者博弈的MATLAB实现:从纳什均衡到Shapley值

三参与者博弈的MATLAB实现:从纳什均衡到Shapley值 简介一份博弈论方向的MATLAB实现代码包面向经济学、计算机科学等专业学生、研究者以及对多主体决策建模感兴趣的工程技术人员可用于课程设计、科研验证与自学实战。内容聚焦3个参与者的非合作博弈与合作博弈场景通过支付矩阵、策略迭代、联盟形成与收益分配等模块帮助读者直观理解纳什均衡、Shapley值等核心概念并可扩展到零和博弈、完全信息博弈等变体。压缩包大小约10.44MB源码以模型仿真与计算为主便于直接运行和二次开发。已有973人学习使用适合有一定MATLAB基础的读者动手实践。通过代码模拟多参与者的策略互动可对比非合作均衡与合作解之间的差异掌握从模型建立、算法实现到结果分析的整体流程。压缩包虽未单独列出文件清单但整体结构便于检索适合作为博弈论计算仿真的入门工具。1. 三参与者非合作博弈与 MATLAB 合作博弈先定均衡概念再写代码看到“新建文件夹_3个参与者的非合作博弈_博弈_MATLAB合作博弈_合作博弈”这个目录名我第一反应是这不是一个能直接跑的成熟项目而是一个需要自己把博弈论概念落到 MATLAB 里的实验任务。要清楚自己在问什么3 个参与者每人有若干策略非合作场景下找纳什均衡合作场景下算特征函数和 Shapley 值。两者在数学上关系密切但在代码上完全两种写法最稳的路径是先确定要哪个解概念再选数据结构。三参与者和两人博弈的区别不只是“多一个人”。两人的 2×2 矩阵可以手算三人每种策略组合都要用三维数组表示联盟数量变成 8 个排列数量变成 6 个。很多从两人博弈迁移过来的代码坏在收益数组的索引顺序和“严格大于还是大于等于”这两个细节上。MATLAB 的优势是数组和排列函数好用劣势是没有官方博弈工具箱所以常见做法是自己写一个最小求解器。本文按非合作、合作两条线走先用三维收益数组穷举纯策略纳什均衡再用特征函数和 Shapley 值做大联盟分配最后给参数敏感性分析和验证技巧。适合要用 MATLAB 做多智能体博弈、资源分配、多人收益分配又不想被抽象博弈论符号卡住的工程师。2. 三参与者非合作博弈的收益表示与纳什均衡搜索2.1 用三维数组存储三人收益矩阵在 MATLAB 里表示三参与者同时出策略最自然的数据结构是三个三维数组payoffA(sA,sB,sC)表示当 A 选第 sA 个策略、B 选第 sB 个策略、C 选第 sC 个策略时 A 拿到的收益payoffB、payoffC 同理。为什么不用 cell 或 struct因为三维数组支持payoffA(:,sb,sc)这种向量化切片后面检查偏离时能一次取出所有改动策略的收益速度最快。% 三人都只有两个策略1合作2背叛 % 维度顺序A策略 x B策略 x C策略 payoffA zeros(2,2,2); payoffB zeros(2,2,2); payoffC zeros(2,2,2); % 用8行组合一次性填充收益避免手动写24个赋值语句 combo [ % sa sb sc uA uB uC 1 1 1 4 4 4 2 1 1 5 1 1 1 2 1 1 5 1 1 1 2 1 1 5 2 2 1 2 2 0 2 1 2 2 0 2 1 2 2 0 2 2 2 2 2 1 1 1 ]; for i 1:size(combo,1) sa combo(i,1); sb combo(i,2); sc combo(i,3); payoffA(sa,sb,sc) combo(i,4); payoffB(sa,sb,sc) combo(i,5); payoffC(sa,sb,sc) combo(i,6); end逻辑说明矩阵每行是一个策略组合后面三列是三个人收益。这样把“策略组合”和“收益”放在同一张表里比在三维数组里凭眼睛找payoffB(2,1,2)直观得多。参数sa/sb/sc是策略编号取值必须与三维数组的第一/二/三维长度一致如果某人有 3 个策略把zeros(2,2,2)改成zeros(3,3,3)combo 矩阵扩展成 27 行即可。注意这个示例拿“合作/背叛”只是为了让策略含义好懂不代表非合作博弈只能用于囚徒困境。换成“报价/不报价”“攻击/防御”都一样。2.2 纯策略纳什均衡的穷举搜索定义一个三元策略组合 (sA,sB,sC) 是纯策略纳什均衡当且仅当没有参与人能通过单独改变自己的策略使自己的收益严格增加。在 MATLAB 里做这个判断不需要调任何工具箱三重循环扫过所有组合即可。isNE false(2,2,2); tol 1e-12; for sa 1:2 for sb 1:2 for sc 1:2 uA payoffA(sa,sb,sc); uB payoffB(sa,sb,sc); uC payoffC(sa,sb,sc); % 检查A固定B、C取A所有策略下的收益 canImproveA any(payoffA(:,sb,sc) uA tol); canImproveB any(payoffB(sa,:,sc) uB tol); canImproveC any(payoffC(sa,sb,:) uC tol); isNE(sa,sb,sc) ~(canImproveA || canImproveB || canImproveC); end end end [idxA, idxB, idxC] ind2sub(size(isNE), find(isNE)); if isempty(idxA) disp(没有纯策略纳什均衡); else for i 1:length(idxA) fprintf(NE: A%d B%d C%d, 收益(%d,%d,%d)\n, ... idxA(i), idxB(i), idxC(i), ... payoffA(idxA(i),idxB(i),idxC(i)), ... payoffB(idxA(i),idxB(i),idxC(i)), ... payoffC(idxA(i),idxB(i),idxC(i))); end end逻辑说明payoffA(:,sb,sc)把 A 的策略编号取遍 1 和 2返回一个长度为 2 的向量再和uA tol做比较只要存在某个策略收益比当前高就认为 A 有偏离动机。加tol是为了处理浮点比较否则两个数学上相等的收益可能因为 1e-16 级的误差被判成“可提高”。参数说明循环上下界的 2 来自策略数如果你把三人策略数改成 3三处循环都要改成 1:3同时isNE也改成false(3,3,3)。穷举的复杂度是策略数的乘积三个人各 2 策略只有 8 个组合三个人各 10 策略也才 1000 个组合在 MATLAB 里毫秒级不需要优化。2.3 纯策略均衡的输出与多均衡判断上面的示例矩阵只有一个纯策略纳什均衡但三参与者博弈经常出现多个均衡。把穷举得到的idxA,idxB,idxC都打印出来就是“多均衡”的完整列表。以下表格列出上面矩阵所有 8 个三元组合方便你对照脚本输出组合 (A,B,C)uAuBuC是否纯策略NE(1,1,1)444否A改选2后收益5(2,1,1)511否B改选2后收益2(1,2,1)151否A改选2后收益2(1,1,2)115否A改选2后收益2(2,2,1)220否C改选2后收益1(2,1,2)202否B改选2后收益1(1,2,2)022否A改选2后收益1(2,2,2)111是多均衡时需要额外的选择准则比如帕累托占优对比每个均衡的收益向量或者“风险占优”。这里不展开但脚本已经把所有候选列出来下一步做筛选就只是数据操作。还有一种情况三人博弈可能连一个纯策略均衡都没有比如三个人玩“石头剪刀布”式的循环偏好。这时候要进入混合策略纳什均衡我在第 4 章给一个用fsolve的兜底搜索。3. MATLAB 合作博弈实现特征函数与三参与者 Shapley 值3.1 特征函数从非合作收益到联盟价值合作博弈和非合作博弈不是两种“算法”而是两种解概念。非合作博弈里 A 背叛 B 这种事随时可能发生合作博弈假设参与者可以签订有约束力的协议收益可以转移支付所以模型的核心变成“每个联盟 S 能创造的总价值 v(S)”。对 3 个参与者共有 2^38 个子集去掉空集后要定义 7 个值。常见做法是按业务语义直接赋值不需要也没办法从非合作收益数组机械换算。比如下面的特征函数表示单人单独干保底收益为 1任意两人结盟能创造 4三人合作能创造 6因为要分给大联盟一个比两人联盟更高的价值才有合作动机联盟成员v(S)∅空0{A}A1{B}B1{C}C1{A,B}A,B4{A,C}A,C4{B,C}B,C4{A,B,C}A,B,C6这个特征函数满足超可加性任意两个不相交联盟合并后价值不低于分开之和。三参与者合作博弈最常用的分配规则是 Shapley 值它把 v(S) 的“边际贡献”按所有参与者到达顺序平均。3.2 三参与者 Shapley 值的全排列实现Shapley 值的公式在教科书里长成带阶乘的求和式但对三参与者直接用perms(1:3)枚举 6 种到达顺序更不容易出错。每个参与者的值等于 6 种顺序下边际贡献的平均值。% 特征函数用有序字符串做key空集单独处理 v containers.Map(); v(A) 1; v(B) 1; v(C) 1; v(AB) 4; v(AC) 4; v(BC) 4; v(ABC) 6; players {A,B,C}; orders perms(1:3); % 6种到达顺序 phi zeros(1,3); for p 1:size(orders,1) order orders(p,:); coalition ; % 已经到达的成员按字典序存避免 AB 与 BA 两种key for k 1:3 i order(k); % 当前参与者编号 before coalition; % 加入之前的联盟 after sort([before, players{i}]); % 加入之后排序保证key唯一 if isempty(before) vBefore 0; % v(∅)0 else vBefore v(before); end vAfter v(after); phi(i) phi(i) (vAfter - vBefore) / size(orders,1); coalition after; end end fprintf(Shapley值: A%.4f, B%.4f, C%.4f\n, phi(1), phi(2), phi(3)); fprintf(大联盟价值%.4f, 分配总量%.4f\n, v(ABC), sum(phi));逻辑说明after sort([before, players{i}])是核心。before已经排序插入新成员后排序保证 Map 的 key 只有 A,B,C,AB,AC,BC,ABC 七种写法。(vAfter - vBefore)/6是每个顺序下的边际贡献因为所有顺序等概率所以直接累加平均。phi(1)/phi(2)/phi(3)分别对应该者在players里的位置。在这个特征函数下运行结果会是 ABC2正好将大联盟价值 6 三等分。如果某个参与者在关键联盟中的边际贡献更大phi就会分化。参数说明修改v即可适配自己的收益分配任务players顺序要与后续使用一一对应。注意三参与者的perms只有 6 行不要担心性能。真正要注意的是containers.Map的 key 区分大小写Ab和AB是两个不同 key全大写只是个人习惯。3.3 用核条件检查 Shapley 值是否稳定Shapley 值是一个“公平”分配但不一定在核core内。三参与者的核由以下不等式定义总额条件xAxBxC v(ABC)单人激励xA ≥ v(A)xB ≥ v(B)xC ≥ v(C)双人激励xAxB ≥ v(AB)xAxC ≥ v(AC)xBxC ≥ v(BC)把上面的 Shapley 值 (2,2,2) 代入总数 6单人和双人条件全部等号成立所以它在核内。如果某个 Shapley 值不满足这些不等式意味着该分配下某个子联盟会脱离大联盟单干合作协议不稳定。此时常见做法是检查你构造的特征函数是否合理或者换用核中心作为分配方案三参与者直接手算六个不等式就够了。4. 三参与者博弈的 MATLAB 参数调优混合策略与敏感性检查4.1 没有纯策略均衡时用 fsolve 搜索混合策略穷举纯策略只是第一步。三个人玩猜拳式的循环偏好或者收益矩阵设计不当都会让纯策略均衡为空。这时必须求混合策略纳什均衡每个参与者用一个概率分布随机选择策略且每个以正概率使用的纯策略期望收益相同且不小于其他策略。常见做法是把问题写成三个方程每个参与者的两个纯策略期望收益差等于 0再用fsolve求解。下面给 A 的方程B、C 照相应换索引即可function eqns mixedNE(x, payoffA, payoffB, payoffC) eqns zeros(3,1); pb [x(2), 1-x(2)]; % B选策略1,2的概率 pc [x(3), 1-x(3)]; % C选策略1,2的概率 % A选择策略1和2的期望收益差 EA1 0; EA2 0; for sb 1:2 for sc 1:2 EA1 EA1 payoffA(1,sb,sc) * pb(sb) * pc(sc); EA2 EA2 payoffA(2,sb,sc) * pb(sb) * pc(sc); end end eqns(1) EA1 - EA2; % B的期望收益注意第一个索引变成A的概率 pa [x(1), 1-x(1)]; EB1 0; EB2 0; for sa 1:2 for sc 1:2 EB1 EB1 payoffB(sa,1,sc) * pa(sa) * pc(sc); EB2 EB2 payoffB(sa,2,sc) * pa(sa) * pc(sc); end end eqns(2) EB1 - EB2; % C的期望收益同理 EC1 0; EC2 0; for sa 1:2 for sb 1:2 EC1 EC1 payoffC(sa,sb,1) * pa(sa) * pb(sb); EC2 EC2 payoffC(sa,sb,2) * pa(sa) * pb(sb); end end eqns(3) EC1 - EC2; end然后在主脚本中调用x0 [0.5, 0.5, 0.5]; opts optimoptions(fsolve, Display, off); x fsolve((x) mixedNE(x, payoffA, payoffB, payoffC), x0, opts); fprintf(混合策略均衡: pA%.4f, pB%.4f, pC%.4f\n, x);注意这个写法直接把x当函数句柄参数mixedNE要放在独立.m文件里或者用局部函数放在脚本末尾。fsolve只能保证找到初始值附近的解三人博弈可能有多个混合均衡建议从不同的x0比如把 0.5 换成 0.1/0.9 组合出发看是否收敛到同一组概率。还要注意fsolve可能收敛到边界解比如 pA0 或 1这表示该参与者实际用的是纯策略你应该回第 2 章的穷举脚本里查一下对应纯策略组合是否真是均衡。如果没有 Optimization Toolbox最经济的替代是固定 pB、pC 后对 A 求最佳反应再做不动点迭代但收敛条件不如fsolve直观。4.2 收益矩阵的一个元素改动能改变多少个均衡前面示例的收益设计只有 (2,2,2) 一个纯策略均衡。我一般会做敏感性检查把某个收益元素当作参数观察均衡数量和均衡位置跳变。比如把payoffB(2,2,2)从 1 改成 0即在所有人都背叛的环境里B 的收益降为 0重新跑纯策略穷举脚本会发现 (2,1,2) 也变成纯策略均衡。原因如下场景payoffB(2,2,2)纯策略NE数量均衡集合原始11(2,2,2)修改后02(2,1,2), (2,2,2)直观解释在 (2,1,2) 组合里B 当前选策略1的收益是 0原来它改选策略2进入全背叛状态能得到 1所以会偏离把payoffB(2,2,2)改成 0 后偏离后收益与当前持平不再是严格改善(2,1,2) 就稳定了。这里要特别强调判定偏离必须用“严格大于”。如果你把代码里的 u tol改成 u那么 (2,1,2) 会被误判为不是均衡因为 B 偏离后收益等于当前收益时会触发“可改善”分支。多人博弈里很多“均衡缺失”的 bug 不是矩阵写错而是比较符用错。这个敏感性测试的做法是用一个循环把payoffB(2,2,2)从 0 到 2 按 0.1 步长扫描对每个值调用纯策略穷举函数记录均衡个数并画成曲线。这样能找到哪些收益临界点会引发均衡跳变适合用来排查多智能体仿真里“突然换策略”的现象。4.3 三参与者代码里三个高频坑第一个坑是三维数组的索引顺序。payoffA(:,sb,sc)中:在第一维取出的是 A 的所有策略如果你写payoffA(sa,:,sc)取出的变成 B 的策略但你以为是 A 的策略均衡判断就全错了。我习惯在代码顶部注释写清“维度顺序 A策略×B策略×C策略”每次引用切片前先默念一次。第二个坑是perms和containers.Map的排序约定。Shapley 值代码里如果coalition不排序BA和AB会是 Map 的不同 key导致 v(BA) 报错。排序再用 Map 是最稳的处理如果参与者的名字是中文或带数字排序规则会变复杂更建议用二进制的联盟编号0 到 7作为特征函数的 key每个参与者在第几位就是第几个 bit。第三个坑是忽略特征函数的合理性。三参与者合作博弈中如果 v(AB) v(A)v(B)那么 A、B 没有理由结盟后面的 Shapley 值再漂亮也没有实际分配意义。用超可加性检查一下任意不相交联盟 S,T 都应该有 v(S∪T) ≥ v(S)v(T)。这个检查只需要一个双重循环但能挡掉一半数据构造错误。5. 验证三参与者 Shapley 值分配的三个快速技巧5.1 用总量和核条件做自检每写完一段 Shapley 值计算先验证两件事sum(phi) v(ABC)以及每个玩家分到的值是否满足核不等式。MATLAB 里可以这样写assert(abs(sum(phi) - v(ABC)) 1e-10, 分配总量不匹配); x phi; assert(x(1) v(A) - 1e-10 x(2) v(B) - 1e-10 ... x(3) v(C) - 1e-10, 单人激励不满足); assert(x(1)x(2) v(AB) - 1e-10 ... x(1)x(3) v(AC) - 1e-10 ... x(2)x(3) v(BC) - 1e-10, 双人激励不满足);5.2 固定一个玩家的策略做降维验证三参与者不好手算时把其中一个参与者的每个策略分别固定三维数组就退化成 2×2 的两人博弈。比如固定 C 选策略1payoffA_2d payoffA(:,:,1)然后对 2×2 矩阵手算或画混合策略反应曲线确认结果与三维穷举一致。这个方法特别适合调试“为什么我的均衡和你手算的不一样”的问题因为每次只核对一个子博弈。5.3 用一个小模板当回归测试三参与者只有 6 个排列手算很容易所以我不建议直接信第三方工具箱但可以用一个小例子验证自己的实现设 v(AB)v(AC)v(BC)4v(ABC)6单人值都为1运行后 phi 应该正好是 [2 2 2]。如果偏了多半是 Map 排序或空集处理出错。这个“单人1、双人4、三人6”的模板可以当回归测试以后每次改特征函数都跑一遍保证框架没被破坏。在实际项目中把三维收益数组、特征函数和分配结果做成三个独立函数参数只传入收益值或联盟价值你的三参与者博弈代码就能直接扩展成资源分配、联合定价、多智能体协作的 MATLAB 实验模块而不需要每次重写求解器。本文还有配套的精品资源点击获取
返回列表