简介:这份资源面向博弈论初学者、高校学生及对策略分析感兴趣的IT从业者,聚焦3个参与者的非合作博弈与合作博弈的MATLAB实现,帮助读者把抽象理论落到可运行的代码上。压缩包为zip格式,整体约10.44MB,包内以MATLAB脚本与函数文件为主,用于构建支付矩阵、迭代求解纳什均衡,并涉及合作博弈中的联盟结构与Shapley值分配等计算,同时可能包含零和、非零和及完全信息等博弈类型的示例代码。已有977人学习下载,说明其在同类资料中具备一定参考价值。读者可借助这些脚本理解博弈模型的建模思路、均衡求解流程与结果验证方法,并在此基础上修改参数、扩展参与者数量或替换支付矩阵,用于课程作业、课题研究或算法复现,是一份偏实践导向的博弈论学习素材。
1. 三个参与者的博弈代码包:从纳什均衡到 Shapley 值,这份 MATLAB 资源能跑出什么
三人博弈是博弈论里一个尴尬的分水岭。两人博弈有成熟的矩阵博弈解法,画个收益矩阵就能手算纳什均衡;参与者一旦变成三个,收益结构从二维矩阵变成三维张量,纯手算基本歇菜,必须借助数值工具。这个压缩包解决的正是这个痛点:它用 MATLAB 把三个参与者的非合作博弈和合作博弈都实现了一遍,非合作侧聚焦纳什均衡的迭代求解,合作侧覆盖联盟博弈和 Shapley 值分配。适合正在做演化博弈、机制设计、资源分配建模的研究生和工程师,也适合想用代码验证课本结论的自学者。它不教你博弈论入门,它给你一套能改参数、能换收益矩阵、能直接出图的工程化脚本。下面按“资源是什么、怎么跑、坑在哪”的顺序拆开讲。
2. 非合作博弈的 MATLAB 落地:三人收益张量与纳什均衡迭代
2.1 三人博弈为什么不能照搬两人矩阵
两人博弈的收益天然是一张二维表,行是玩家 A 的策略,列是玩家 B 的策略,查表即得。三人博弈的收益是三维的:玩家 1 选策略 i、玩家 2 选策略 j、玩家 3 选策略 k,收益落在 (i, j, k) 这个坐标上。MATLAB 里表示它的自然结构是三维数组,而不是矩阵。很多人第一次写三人博弈代码,习惯性用两个二维矩阵拼,结果在找最优反应时索引错位,跑出来的“均衡”根本不自洽。常见做法是用zeros(n1, n2, n3)建张量,三个维度分别对应三个参与者的策略数,每个格点存一个长度为 3 的收益向量,或者干脆建三个同尺寸张量,分别存三人的收益。这样在计算某个参与者的最优反应时,固定另外两个维度,沿自己的维度取max,逻辑清晰不易错。
2.2 用最优反应迭代逼近纳什均衡
纳什均衡的定义是:给定其他参与者的策略,没有人能通过单方面改变策略提高收益。工程上最直接的求法是最优反应迭代——轮流让每个参与者对自己的策略做一次最优反应,循环直到策略组合不再变化。下面是一段可直接运行的最小实现,收益张量用随机数占位,替换成你自己的数据即可。
% 三人非合作博弈:最优反应迭代求纯策略纳什均衡 rng(1); n1 = 3; n2 = 3; n3 = 3; % 三个参与者的策略数 % 三个收益张量,U{p}(i,j,k) 是参与者 p 在策略组合 (i,j,k) 下的收益 U = cell(1,3); for p = 1:3 U{p} = rand(n1, n2, n3) * 10; % 占位数据,替换为真实收益 end s = [1 1 1]; % 初始策略组合 maxIter = 100; for iter = 1:maxIter sOld = s; for p = 1:3 % 固定其他两人的策略,沿参与者 p 的维度找最优反应 idx = num2cell(s); % 当前策略组合 switch p case 1 payoff = squeeze(U{p}(:, s(2), s(3))); case 2 payoff = squeeze(U{p}(s(1), :, s(3))); case 3 payoff = squeeze(U{p}(s(1), s(2), :)); end [~, s(p)] = max(payoff); % 取收益最大的策略索引 end if isequal(s, sOld) fprintf('第 %d 轮收敛,纳什均衡策略组合: [%d %d %d]\n', iter, s); break; end end if iter == maxIter fprintf('达到最大迭代次数仍未收敛,可能存在混合策略均衡\n'); end逻辑说明:外层循环控制迭代轮次,内层for p = 1:3依次让每个参与者做最优反应。squeeze的作用是把固定两个维度后剩下的那一维抽出来,变成向量再取max。num2cell(s)那行是为后续扩展成混合策略预留的索引写法,纯策略场景下用s(2)、s(3)直接索引也行。参数上,n1/n2/n3改成你的实际策略数,U{p}换成真实收益张量,maxIter一般设 100 到 500 足够,纯策略均衡通常几十轮内收敛。如果跑满maxIter还没收敛,大概率这个博弈没有纯策略纳什均衡,需要转向混合策略,用线性规划或支持枚举法求解,这是三人博弈里最常见的翻车点。
2.3 混合策略均衡的补充思路
纯策略不收敛时,混合策略是退路。三人混合策略纳什均衡的求解比两人复杂得多,两人可以用线性规划(Lemke-Howson 算法),三人一般用支持枚举(support enumeration)或非线性方程求解。常见做法是枚举每个参与者的策略子集作为“支持集”,在该支持集上令各策略的期望收益相等,解方程组,再验证非支持集策略的收益不超过均衡收益。MATLAB 里可以用fsolve解这个非线性方程组,但要注意初值敏感,多试几组初值。压缩包里如果带了混合策略脚本,重点看它怎么构造期望收益矩阵和怎么处理支持集组合爆炸——三人各 3 个策略,支持集组合就有 (2^3-1)^3 = 343 种,规模再大就得换算法。
3. 合作博弈的 MATLAB 落地:联盟特征函数与 Shapley 值计算
3.1 联盟博弈的特征函数怎么建
合作博弈的核心是联盟特征函数 v(S),它定义每个可能的联盟 S 能创造多少总收益。三个参与者时,可能的联盟有:单人联盟 {1}{2}{3}、双人联盟 {1,2}{1,3}{2,3}、大联盟 {1,2,3},共 7 个非空联盟。特征函数必须满足超可加性:两个不相交联盟合并后的收益不低于各自收益之和,否则合作没有意义。在 MATLAB 里,最省事的表示是一个长度为 2^3 - 1 = 7 的向量,按二进制位映射联盟成员。下面这段代码演示怎么建特征函数并检查超可加性。
% 三人合作博弈:特征函数与超可加性检查 n = 3; % v 的索引用二进制表示联盟:1={1}, 2={2}, 3={1,2}, 4={3}, 5={1,3}, 6={2,3}, 7={1,2,3} v = [10, 12, 30, 8, 26, 28, 50]; % 占位数据,替换为真实特征函数 % 检查超可加性:任意两个不相交联盟 S、T,v(S)+v(T) <= v(S∪T) for S = 1:7 for T = 1:7 if bitand(S, T) == 0 % 不相交 ST = bitor(S, T); if v(S) + v(T) > v(ST) + 1e-9 fprintf('违反超可加性: v(%d)+v(%d)=%g > v(%d)=%g\n', ... S, T, v(S)+v(T), ST, v(ST)); end end end end fprintf('超可加性检查完成\n');逻辑说明:用二进制位表示联盟成员,bitand(S,T)==0判断两个联盟不相交,bitor(S,T)得到合并后的联盟索引。1e-9是浮点容差,避免因舍入误差误报。参数上,v向量按你的实际问题填,索引顺序要固定好,否则后面算 Shapley 值会错位。这段代码跑完如果没有任何输出,说明特征函数满足超可加性,合作有理论依据;如果有违反,要么数据有问题,要么这个场景本身不适合用合作博弈建模。
3.2 Shapley 值的三种算法与代码实现
Shapley 值回答的是“每个参与者对联盟的边际贡献平均是多少”,是合作博弈里最常用的公平分配方案。三人场景下公式展开是:
φ_i = Σ_{S⊆N{i}} [|S|! (n-|S|-1)! / n!] × [v(S∪{i}) - v(S)]
直接按公式枚举所有子集即可,三人只有 7 个联盟,计算量可忽略。下面给出直接枚举的实现。
% 三人 Shapley 值:按定义直接枚举子集 n = 3; v = [10, 12, 30, 8, 26, 28, 50]; % 与上节一致的特征函数 phi = zeros(1, n); for i = 1:n others = setdiff(1:n, i); % 除 i 外的参与者 % 枚举 others 的所有子集 for mask = 0:(2^length(others)-1) S = []; % 联盟 S 的成员 for b = 1:length(others) if bitand(mask, 2^(b-1)) S = [S, others(b)]; end end s = length(S); % 联盟 S 的二进制索引 idxS = 0; for m = S idxS = idxS + 2^(m-1); end % 联盟 S∪{i} 的二进制索引 idxSi = idxS + 2^(i-1); vS = 0; if idxS > 0, vS = v(idxS); end vSi = v(idxSi); weight = factorial(s) * factorial(n - s - 1) / factorial(n); phi(i) = phi(i) + weight * (vSi - vS); end end fprintf('Shapley 值: φ1=%.4f, φ2=%.4f, φ3=%.4f\n', phi(1), phi(2), phi(3)); fprintf('Shapley 值之和 = %.4f, 大联盟收益 = %.4f\n', sum(phi), v(7));逻辑说明:外层遍历每个参与者 i,内层用位掩码枚举其余参与者的所有子集 S。idxS和idxSi分别算出 S 和 S∪{i} 在特征函数向量里的索引。权重factorial(s)*factorial(n-s-1)/factorial(n)就是 Shapley 公式里的组合系数。跑完检查sum(phi)是否等于v(7),这是 Shapley 值的有效性公理,如果不等说明特征函数或索引有误。参数上,v向量必须和上一节一致,n改成实际参与者数后这段代码也能扩展到更多人,但枚举量随 n 指数增长,n 超过 10 就得换蒙特卡洛近似或排列采样法。
3.3 合作博弈的稳定性:核心与夏普利值的配合
Shapley 值给出一个公平分配,但不保证这个分配落在“核心”里。核心是指没有任何联盟能通过脱离大联盟获得更好收益的分配集合。三人场景下,核心是一组线性不等式约束,可以用linprog检查某个分配是否在核心内。常见做法是:先算 Shapley 值,再把它代入核心约束验证,如果不在核心内,说明这个博弈的公平分配和稳定分配存在冲突,需要引入其他解概念(如核仁 nucleolus)。压缩包里如果带了核心计算脚本,重点看它怎么把联盟约束写成A*x <= b的形式,这是合作博弈代码里最容易写错的部分。
4. 避坑与排查:三人博弈代码跑不通的五个血泪经验
4.1 现象:最优反应迭代震荡不收敛,原因:存在混合策略均衡或循环最优反应
三人博弈里,纯策略纳什均衡不一定存在。如果迭代在几个策略组合之间来回跳,不是代码写错了,是这个博弈本身没有纯策略均衡。解决办法是转向混合策略求解,或者检查收益张量是否满足某些存在性条件(比如超模博弈)。我一般会先跑一遍纯策略迭代,不收敛就直接切混合策略脚本,不在这上面浪费时间。
4.2 现象:Shapley 值之和与大联盟收益对不上,原因:特征函数索引映射错误
Shapley 值必须满足有效性公理,和等于大联盟收益。对不上几乎一定是索引问题:要么v向量的顺序和二进制映射不一致,要么idxSi计算时漏了某个位。排查方法是手动算一个两人的简单例子,逐步打印每个联盟的索引和取值,对一遍就清楚了。这个坑我踩过不止一次,后来养成习惯:特征函数建好后先跑超可加性检查,再跑 Shapley 值,两步都过了才信结果。
4.3 现象:squeeze之后维度不对,原因:某一维长度为 1 时被压掉
squeeze会删掉所有长度为 1 的维度。如果某个参与者的策略数恰好是 1,squeeze后的向量维度会和你预期的不一样,导致max取错方向。解决办法是用reshape显式指定目标维度,或者用U{p}(:, s(2), s(3))这种带冒号的索引直接取,不用squeeze。这个坑在策略数动态变化时特别隐蔽,建议代码里加一句assert检查维度。
4.4 现象:混合策略fsolve不收敛或解出负概率,原因:初值不当或方程组欠定
三人混合策略均衡的方程组可能有多个解,fsolve对初值敏感。解出负概率说明约束没加对,需要在方程组里加入概率非负和求和为 1 的约束,或者用lsqnonlin带边界求解。常见做法是多跑几组随机初值,取满足所有约束的解,再验证是否真的是均衡。
4.5 现象:合作博弈核心为空但 Shapley 值照常算出,原因:没做核心存在性检查
Shapley 值总是能算出来,但核心可能为空。核心为空意味着不存在任何分配能同时满足所有联盟的理性约束,合作博弈的稳定性没有保障。排查方法是构造核心的线性不等式组,用linprog找可行解,如果无可行解就说明核心为空。这时候 Shapley 值只能作为参考,不能当作稳定分配方案用。
5. 进阶技巧:把三人博弈脚本改造成可复用的参数化工具
跑通单个案例只是第一步,真正省时间的是把脚本改造成参数化函数,换一组收益数据就能直接出结果。我一般会把非合作和合作两部分封装成两个函数:nash3(U)接收收益张量返回纯策略均衡和收敛信息,shapley3(v)接收特征函数返回 Shapley 值和核心检查结果。这样在批量实验时,外层只需要循环换数据,不用每次改代码。
验证结果是否正确,除了前面说的有效性公理和超可加性检查,还有一个实用手段:用已知解析解的经典博弈做回归测试。比如三人囚徒困境、三人公共品博弈,这些有文献给出的均衡结果,拿你的代码跑一遍对答案,对上了再跑自己的数据。这个习惯帮我省了很多次返工。
参数化时注意几个边界:策略数不要硬编码成 3,用size(U{1})动态取;特征函数向量的长度用2^n - 1算,别写死 7;Shapley 值的枚举在 n 超过 8 之后明显变慢,提前想好要不要换蒙特卡洛近似。还有一个容易忽略的点:MATLAB 的factorial在 n 较大时会溢出,n 超过 170 直接返回 Inf,虽然三人场景碰不到,但改造成通用工具时最好换成gammaln做对数运算再取指数。
从那以后我每次拿到新的博弈数据,都强制先跑超可加性检查和有效性验证,两步都过了才看具体数值。这个习惯让我少走了很多弯路。希望帮到你。
本文还有配套的精品资源,点击获取