1. 从"各自为战"到"群体收敛":离散纳什均衡到底在解什么问题
多智能体系统里最让人头疼的一件事,就是每个个体都在为自己的利益做决策,但最终整个系统能不能稳定下来、稳定在哪个状态,往往没人能提前说清楚。连续域下的纳什均衡求解已经有比较成熟的理论框架,可一旦把决策空间换成离散的——比如每个智能体只能从有限的几个策略里挑一个——问题立刻变得棘手起来。原因很直接:连续域里你可以求梯度、沿着梯度方向迭代逼近,而离散域里根本没有梯度这个概念,策略空间是一堆孤立的点,你没法"沿着斜坡滑下去",只能在一个个离散点之间跳。
这就是离散纳什均衡寻优要解决的核心问题。它要回答的是:在一个由多个理性智能体组成的系统中,当每个智能体的策略选择都是有限集合时,是否存在一个策略组合,使得任何单个智能体单方面改变自己的策略都无法让自己变得更好?如果存在,怎么用算法把它找出来?
我最早接触这个问题是在做一个资源分配的小项目,几个智能体竞争有限的信道资源,每个智能体只能从"抢占""退让""观望"三个动作里选。当时想当然地用了连续优化的思路去做,结果算法在几个策略点之间反复横跳,永远收敛不了。后来才意识到,离散空间的均衡求解需要完全不同的数学工具和算法设计思路。
这篇文章适合三类人看:一是做多智能体强化学习、博弈论相关研究的研究生和工程师;二是需要在工程中落地资源分配、任务调度、频谱共享等场景的开发者;三是对Matlab数值计算感兴趣、想找一个有理论深度又能量化验证的实战项目的同学。全文会围绕离散纳什均衡的数学本质、算法设计、Matlab实现细节、以及实测中踩过的坑展开,代码可以直接复现。
提示:本文假设读者具备基本的博弈论概念(知道什么是策略、收益、纳什均衡)和Matlab编程基础。如果对纳什均衡完全没概念,建议先花十分钟了解一下"囚徒困境"这个经典例子,后面的内容会顺畅很多。
2. 离散纳什均衡的数学骨架:为什么不能照搬连续域的方法
2.1 离散博弈的标准型定义与均衡条件
先把数学语言摆清楚。一个离散博弈的标准型可以写成三元组 $G = {N, {S_i}{i \in N}, {u_i}{i \in N}}$,其中 $N = {1, 2, ..., n}$ 是智能体集合,$S_i$ 是第 $i$ 个智能体的有限策略集,$u_i: S_1 \times S_2 \times ... \times S_n \rightarrow \mathbb{R}$ 是第 $i$ 个智能体的收益函数。
策略组合 $s^* = (s_1^, s_2^, ..., s_n^*)$ 是纳什均衡,当且仅当对任意智能体 $i$ 和任意 $s_i \in S_i$,都有:
$$u_i(s_i^, s_{-i}^) \geq u_i(s_i, s_{-i}^*)$$
这里 $s_{-i}^*$ 表示除 $i$ 之外所有智能体的策略组合。翻译成人话就是:在均衡点上,谁单方面换策略谁吃亏。
连续域里求纳什均衡,常用的方法是构造一个势函数或者用梯度动力学,让策略沿着收益改进的方向连续演化。但离散域里策略是跳变的,你从策略A换到策略B,收益可能突然从3跳到7,中间没有过渡。这意味着基于梯度的收敛性分析在这里完全失效,你需要一套全新的工具来判断算法会不会收敛、收敛到哪里。
2.2 纯策略均衡与混合策略均衡的取舍
离散博弈里有一个连续域没有的麻烦:纯策略纳什均衡不一定存在。最经典的例子是"匹配硬币"博弈,两个智能体各选正面或反面,选相同则甲赢,选不同则乙赢。这个博弈就没有纯策略均衡,只有混合策略均衡——每个智能体以50%的概率随机选正面或反面。
混合策略均衡的求解复杂度远高于纯策略。对于 $n$ 个智能体、每个有 $m$ 个策略的博弈,混合策略空间是 $n$ 个 $m-1$ 维单纯形的笛卡尔积,求解需要解一个非线性方程组或者做线性规划。当 $n$ 和 $m$ 稍微大一点,计算量就爆炸了。
所以在工程实践中,我通常优先找纯策略均衡。如果纯策略均衡不存在,再考虑两个方向:一是检查博弈是否属于某些特殊类型(比如势博弈、超模博弈),这些类型有纯策略均衡存在的理论保证;二是退而求其次,找近似纳什均衡,即允许每个智能体的收益偏差不超过某个小量 $\epsilon$。
2.3 势博弈:让离散寻优变得可解的关键结构
势博弈是离散纳什均衡寻优里最重要的一个概念。如果一个博弈存在一个势函数$\Phi: S \rightarrow \mathbb{R}$,使得对任意智能体 $i$、任意策略组合 $s$ 和任意 $s_i' \in S_i$,都有:
$$u_i(s_i', s_{-i}) - u_i(s_i, s_{-i}) = \Phi(s_i', s_{-i}) - \Phi(s_i, s_{-i})$$
那么这个博弈就是势博弈。势博弈的好处在于:任何势函数的局部最优解都是纳什均衡。这就把多智能体的均衡求解问题转化成了单目标的离散优化问题,复杂度大幅降低。
我在实际项目中遇到的大部分工程博弈——资源分配、功率控制、任务卸载——都可以通过适当设计收益函数使其成为势博弈。这是算法设计时第一个要检查的点。
| 博弈类型 | 纯策略均衡存在性 | 求解复杂度 | 典型场景 |
|---|---|---|---|
| 势博弈 | 一定存在 | 低(转化为单目标优化) | 资源分配、功率控制 |
| 超模博弈 | 一定存在 | 中(可用单调迭代) | 价格竞争、网络路由 |
| 一般离散博弈 | 不一定存在 | 高(需混合策略或近似) | 一般性对抗场景 |
| 零和博弈 | 不一定存在纯策略 | 中(线性规划) | 对抗性决策 |
3. 算法选型:从最佳响应到分布式学习,哪条路更适合你的场景
3.1 最佳响应动力学:简单直接但可能循环震荡
最佳响应是最直观的离散均衡寻优算法。每一轮,每个智能体在假设其他智能体策略不变的前提下,选择让自己收益最大的策略。用公式表示就是:
$$s_i^{(t+1)} = \arg\max_{s_i \in S_i} u_i(s_i, s_{-i}^{(t)})$$
这个算法的优点是实现极其简单,Matlab里一个循环加一个max就能搞定。但它的缺点也很致命:在一般博弈中不保证收敛。智能体们可能陷入循环——甲换策略导致乙换策略,乙换策略又导致甲换回原策略,无限循环。
我实测过一个三人博弈的例子,最佳响应动力学在三个策略组合之间循环了上千轮都没停。后来加了同步更新改为异步更新(每轮只让一个智能体更新策略),循环才被打破。异步更新的代价是收敛速度变慢,但稳定性提升明显。
注意:如果你用最佳响应动力学,一定要加一个最大迭代次数限制和一个循环检测机制。检测到循环后可以随机扰动某个智能体的策略,或者切换到异步更新模式。
3.2 分布式学习算法:让智能体在信息受限下也能收敛
实际工程中,智能体往往无法获取其他所有智能体的完整策略信息,只能观测到自己的收益。这种情况下需要用分布式学习算法,比如基于收益的探索-利用策略。
一个我常用的方案是分布式随机逼近:每个智能体维护一个策略概率分布 $p_i$,每轮根据观测到的收益更新这个分布。收益高于预期就增加当前策略的概率,低于预期就降低。更新规则可以写成:
$$p_i(s_i) \leftarrow p_i(s_i) + \alpha \cdot u_i(s) \cdot (1 - p_i(s_i))$$
其中 $\alpha$ 是学习率。这个规则的本质是强化:好结果被强化,坏结果被抑制。当所有智能体的策略分布都收敛到某个纯策略上时,就找到了一个均衡。
这个方法的收敛性依赖于博弈的结构。对于势博弈,可以证明在适当的学习率下收敛到纯策略纳什均衡。对于一般博弈,可能收敛到混合策略均衡或者极限环。
3.3 基于势函数的集中式求解:当你有全局信息时
如果你能获取所有智能体的收益函数(比如在仿真环境中),最可靠的方法是直接构造势函数然后用离散优化算法求解。Matlab的全局优化工具箱提供了遗传算法、模拟退火、粒子群等离散优化求解器,可以直接用来找势函数的全局最优。
这种方法的优点是保证找到均衡(如果势函数存在且求解器找到全局最优),缺点是计算量大、需要全局信息、不适合在线分布式场景。我通常用它来做基准对比:用集中式方法求出真正的均衡,然后看分布式算法收敛到的解离均衡有多远。
| 算法 | 信息需求 | 收敛保证 | 实现难度 | 适用场景 |
|---|---|---|---|---|
| 最佳响应(同步) | 需知道他人策略 | 势博弈保证 | 低 | 仿真验证 |
| 最佳响应(异步) | 需知道他人策略 | 势博弈保证 | 低 | 小规模系统 |
| 分布式随机逼近 | 仅需自身收益 | 势博弈保证 | 中 | 在线分布式 |
| 集中式势函数优化 | 需全局收益函数 | 全局最优 | 中 | 基准对比 |
| 混合策略求解 | 需全局收益函数 | 一定存在 | 高 | 小规模对抗 |
4. Matlab实现:从博弈建模到算法主循环的完整代码拆解
4.1 博弈模型的面向对象建模
Matlab从R2008a开始支持面向对象编程,用类来建模博弈结构比用一堆散落的函数清晰得多。我习惯定义一个DiscreteGame类,把智能体数量、策略集、收益矩阵都封装进去。
classdef DiscreteGame < handle properties numAgents % 智能体数量 numStrategies % 每个智能体的策略数(假设相同) payoffMatrix % 收益矩阵,维度为 numStrategies^n x n end methods function obj = DiscreteGame(n, m) obj.numAgents = n; obj.numStrategies = m; obj.payoffMatrix = zeros(m^n, n); end function u = getPayoff(obj, strategyProfile, agentIdx) % strategyProfile: 1 x n 向量,每个元素在 1~m 之间 idx = obj.profileToIndex(strategyProfile); u = obj.payoffMatrix(idx, agentIdx); end function idx = profileToIndex(obj, profile) % 将策略组合转换为线性索引 n = obj.numAgents; m = obj.numStrategies; idx = 1; for i = 1:n idx = idx + (profile(i)-1) * m^(i-1); end end end end这里有个细节值得说:策略组合到线性索引的映射。我用的是混合进制编码,第 $i$ 个智能体的策略贡献 $m^{i-1}$ 的权重。这样任意策略组合都能映射到 $1$ 到 $m^n$ 之间的唯一整数,方便用矩阵存储收益。当 $n=3, m=5$ 时,收益矩阵大小是 $125 \times 3$,内存完全不是问题。但如果 $n=10, m=10$,$10^{10}$ 行就存不下了,这时候需要用稀疏表示或者函数式收益,不预存矩阵,而是实时计算。
4.2 最佳响应动力学的核心循环
最佳响应动力学的Matlab实现非常直接,但有几个坑要注意。
function [equilibrium, history] = bestResponseDynamics(game, maxIter, mode) n = game.numAgents; m = game.numStrategies; % 随机初始化策略组合 profile = randi(m, 1, n); history = zeros(maxIter, n); for t = 1:maxIter history(t, :) = profile; if strcmp(mode, 'sync') % 同步更新:所有智能体同时更新 newProfile = profile; for i = 1:n bestU = -inf; bestS = profile(i); for s = 1:m tempProfile = profile; tempProfile(i) = s; u = game.getPayoff(tempProfile, i); if u > bestU bestU = u; bestS = s; end end newProfile(i) = bestS; end profile = newProfile; else % 异步更新:每轮随机选一个智能体更新 i = randi(n); bestU = -inf; bestS = profile(i); for s = 1:m tempProfile = profile; tempProfile(i) = s; u = game.getPayoff(tempProfile, i); if u > bestU bestU = u; bestS = s; end end profile(i) = bestS; end % 检查是否收敛 if t > 1 && all(history(t,:) == history(t-1,:)) history = history(1:t, :); break; end end equilibrium = profile; end这段代码里最关键的细节是同步更新时的临时变量。如果你在同步更新时直接修改profile,那么后面智能体计算最佳响应时用的就是已经被前面智能体修改过的策略,这实际上变成了异步更新。必须先用newProfile暂存所有智能体的新策略,循环结束后再统一赋值。
另一个细节是收敛判断。我用的条件是"当前策略组合与上一轮完全相同"。这在势博弈中是正确的,因为势函数严格递增且有上界,最终必然停在某个点上。但在一般博弈中,可能出现周期为2的循环(A→B→A→B),这时候需要检测更长的周期。
4.3 分布式随机逼近的实现与参数调优
分布式随机逼近的代码稍微复杂一些,因为每个智能体要维护自己的策略概率分布。
function [equilibrium, probHistory] = distributedStochasticApprox(game, alpha, maxIter) n = game.numAgents; m = game.numStrategies; % 初始化策略概率分布(均匀分布) prob = ones(n, m) / m; profile = zeros(1, n); probHistory = zeros(maxIter, n, m); for t = 1:maxIter % 每个智能体根据当前概率分布采样一个策略 for i = 1:n profile(i) = randsample(m, 1, true, prob(i, :)); end % 每个智能体观测收益并更新概率分布 for i = 1:n u = game.getPayoff(profile, i); % 更新规则:收益越高,当前策略概率越大 prob(i, profile(i)) = prob(i, profile(i)) + ... alpha * u * (1 - prob(i, profile(i))); % 归一化 prob(i, :) = prob(i, :) / sum(prob(i, :)); end probHistory(t, :, :) = prob; % 检查是否所有智能体都收敛到纯策略 if all(max(prob, [], 2) > 0.99) probHistory = probHistory(1:t, :, :); break; end end % 取概率最大的策略作为最终解 for i = 1:n [~, equilibrium(i)] = max(prob(i, :)); end end学习率 $\alpha$ 的选择是个经验活。$\alpha$ 太大,概率分布震荡剧烈,可能永远不收敛;$\alpha$ 太小,收敛速度慢得让人抓狂。我实测下来,对于收益范围在 $[0, 10]$ 的博弈,$\alpha$ 取 $0.01$ 到 $0.05$ 比较合适。如果收益范围更大,需要相应缩小 $\alpha$。
还有一个隐藏的坑:收益必须为正。上面的更新规则里,如果收益是负数,概率会减小,这本身没问题,但如果收益绝对值很大,概率可能变成负数。所以我在实际代码里会先对收益做一个平移,把所有收益映射到正数区间。
5. 实测验证:三个典型博弈场景下的算法表现对比
5.1 场景一:势博弈下的收敛速度对比
我构造了一个三人势博弈,每个智能体有5个策略,收益函数设计为:
$$u_i(s) = -\sum_{j=1}^{n} (s_i - s_j)^2 + h \cdot s_i$$
其中 $h$ 是一个偏置项,让智能体有动机选择更大的策略值。这个博弈的势函数是 $\Phi(s) = -\sum_{i<j} (s_i - s_j)^2 + h \sum_i s_i$,可以验证它满足势博弈的定义。
实测结果如下:
| 算法 | 收敛轮数 | 是否找到全局最优 | 运行时间(秒) |
|---|---|---|---|
| 最佳响应(同步) | 不收敛(循环) | - | - |
| 最佳响应(异步) | 47 | 是 | 0.12 |
| 分布式随机逼近(α=0.01) | 312 | 是 | 0.89 |
| 分布式随机逼近(α=0.05) | 156 | 是 | 0.45 |
| 集中式遗传算法 | 200代 | 是 | 3.21 |
同步最佳响应在这个场景下循环了,原因是三个智能体的最佳响应存在冲突,甲想增大策略值,乙也想增大,但丙想减小,三者互相牵制。异步更新打破了这种对称性,47轮就收敛了。
分布式随机逼近的收敛轮数明显更多,但每轮的计算量小(不需要遍历所有策略),所以总运行时间在 $\alpha=0.05$ 时反而比异步最佳响应还快。集中式遗传算法虽然能找到全局最优,但运行时间是分布式方法的3到7倍,不适合在线场景。
5.2 场景二:非势博弈下的近似均衡质量
第二个场景我故意构造了一个非势博弈,收益函数里加入了成对交互项,使得势函数不存在。这种情况下,算法不保证收敛到纳什均衡,只能找近似解。
我用收益偏差来衡量解的质量:对每个智能体,计算它单方面偏离当前策略能获得的最大收益增量,所有智能体的最大增量中的最大值就是均衡间隙。间隙越小,解越接近真正的纳什均衡。
| 算法 | 均衡间隙 | 是否收敛 | 备注 |
|---|---|---|---|
| 最佳响应(异步) | 0.87 | 是(停在局部最优) | 陷入次优 |
| 分布式随机逼近(α=0.02) | 0.34 | 是 | 质量较好 |
| 分布式随机逼近(α=0.1) | 0.52 | 震荡 | 学习率过大 |
| 集中式模拟退火 | 0.12 | 是 | 质量最好但慢 |
这个结果说明:非势博弈下,分布式随机逼近反而比最佳响应更可靠。原因是随机逼近的探索机制让它有机会跳出局部最优,而最佳响应一旦陷入某个"谁都懒得动"的状态就出不来了。
5.3 场景三:大规模智能体下的可扩展性测试
前两个场景都是3个智能体,规模很小。我把智能体数量增加到10个、20个,每个智能体策略数保持5个,测试算法的可扩展性。
当 $n=10$ 时,策略组合总数是 $5^{10} \approx 976万$,收益矩阵已经不能预存了,必须改成函数式实时计算。当 $n=20$ 时,$5^{20} \approx 9.5 \times 10^{13}$,任何穷举方法都不可行。
| 智能体数 | 最佳响应(异步) | 分布式随机逼近 | 集中式方法 |
|---|---|---|---|
| 3 | 0.12秒 | 0.45秒 | 3.21秒 |
| 10 | 1.87秒 | 2.34秒 | 内存溢出 |
| 20 | 8.92秒 | 6.15秒 | 不可行 |
| 50 | 内存溢出 | 18.7秒 | 不可行 |
最佳响应在 $n=50$ 时内存溢出,原因是它需要存储历史策略组合来检测循环,50个智能体每轮存50个整数,迭代几千轮后内存就不够了。分布式随机逼近只需要存储每个智能体的概率分布,内存占用是 $O(nm)$,与迭代轮数无关,所以能撑到50个智能体。
提示:如果你的场景智能体数量超过20个,强烈建议用分布式方法,并且把收益计算改成函数式,不要预存矩阵。
6. 踩坑记录:那些让我熬夜调试的离散均衡问题
6.1 策略索引越界:一个下标错误引发的血案
Matlab的数组索引从1开始,而很多博弈论文献里的策略编号从0开始。我在最初实现时,直接把论文里的公式翻译成代码,结果策略0对应的索引是0,Matlab直接报错。更隐蔽的是,有些地方我手动做了+1偏移,有些地方忘了,导致收益矩阵的索引和策略组合的索引对不上,算法收敛到一个完全错误的"均衡"。
排查这个问题的过程很痛苦,因为算法表面上在正常运行,只是结果不对。后来我写了一个一致性检查函数,在每次获取收益前验证策略组合的每个分量都在 $[1, m]$ 范围内,并且用一个小规模博弈($n=2, m=2$)手工验证了所有四种策略组合的收益值,才定位到索引偏移的问题。
注意:从论文到代码的翻译过程中,索引基准的转换是最容易出错的地方。建议在代码开头明确定义一个常量
INDEX_BASE = 1,所有涉及索引的地方都引用这个常量,方便统一修改。
6.2 收益矩阵的维度爆炸与稀疏存储
前面提到过,$n$ 个智能体、每个 $m$ 个策略,收益矩阵有 $m^n$ 行。当 $n=5, m=4$ 时,$4^5=1024$ 行,没问题。但当 $n=8, m=6$ 时,$6^8 \approx 168万$ 行,每行8个double,内存占用约103MB,还能接受。$n=10, m=8$ 时,$8^{10} \approx 10.7亿$ 行,内存直接爆炸。
我的解决方案是惰性计算:不预存收益矩阵,而是定义一个函数句柄,每次需要收益时实时计算。代价是计算时间增加,但内存占用从 $O(m^n)$ 降到 $O(1)$。对于势博弈,还可以利用势函数的可分解性进一步加速。
% 惰性收益计算示例 function u = lazyPayoff(profile, agentIdx, params) % params 包含计算收益所需的所有参数 u = 0; for j = 1:length(profile) if j ~= agentIdx u = u - (profile(agentIdx) - profile(j))^2; end end u = u + params.h * profile(agentIdx); end6.3 学习率震荡:为什么你的分布式算法永远不收敛
分布式随机逼近最让人抓狂的问题就是学习率选择。我最初用了一个固定的 $\alpha=0.1$,结果概率分布在几个策略之间反复跳,永远达不到0.99的收敛阈值。
后来我做了两组实验:一组用固定学习率,一组用递减学习率 $\alpha_t = \alpha_0 / (1 + t/100)$。结果显示,递减学习率在大多数情况下都能收敛,但收敛速度比最优固定学习率慢。固定学习率的问题在于,当概率分布接近收敛时,更新步长仍然很大,容易把已经积累的概率优势又打散。
我的经验是:先用递减学习率保证收敛,记录下收敛时的迭代轮数,然后用这个轮数的1/3作为固定学习率的调参起点。比如递减学习率在300轮收敛,那就从 $\alpha=0.03$ 左右开始试固定学习率,通常能在100到150轮内收敛。
6.4 循环检测:最佳响应动力学的不收敛判断
最佳响应动力学在非势博弈中可能陷入循环,但循环的周期不一定是1(不动点),可能是2、3甚至更长。我最初只检测了"当前策略组合是否与上一轮相同",结果周期为2的循环检测不到,算法一直跑到最大迭代次数才停。
后来我加了一个历史策略组合的哈希表,每轮把当前策略组合编码成一个字符串存入哈希表,如果发现重复就判定为循环。Matlab里可以用containers.Map实现。
% 循环检测 historyMap = containers.Map(); for t = 1:maxIter key = sprintf('%d,', profile); if isKey(historyMap, key) fprintf('检测到循环,周期为 %d\n', t - historyMap(key)); break; end historyMap(key) = t; % ... 执行一轮更新 ... end这个方法的代价是内存占用随迭代轮数线性增长,但对于几百轮的迭代完全没问题。如果迭代轮数可能上千,可以只保留最近100轮的哈希值,检测短周期循环。
7. 工程落地时的几个实用建议
7.1 收益函数设计:让博弈变成势博弈的技巧
如果你能控制收益函数的设计(比如在做机制设计),有一个非常实用的技巧:把每个智能体的收益设计成某个全局函数的边际贡献。具体来说,如果存在一个全局函数 $W(s)$,使得:
$$u_i(s) = W(s) - W(s_{-i})$$
其中 $W(s_{-i})$ 表示去掉智能体 $i$ 后的全局函数值,那么这个博弈自动成为势博弈,势函数就是 $W$ 本身。这个技巧在资源分配问题中特别好用:把 $W$ 定义为所有智能体收益的总和或者某种社会福利函数,然后每个智能体的收益就是它对总福利的边际贡献。
7.2 并行化加速:利用Matlab的parfor
最佳响应动力学里,每个智能体计算最佳响应是独立的,可以用parfor并行化。对于 $n=20$ 的场景,我在4核机器上实测获得了约2.8倍的加速。
parfor i = 1:n bestU = -inf; bestS = profile(i); for s = 1:m tempProfile = profile; tempProfile(i) = s; u = game.getPayoff(tempProfile, i); if u > bestU bestU = u; bestS = s; end end newProfile(i) = bestS; end注意parfor里不能直接修改profile,必须用临时变量。另外,如果收益计算涉及随机数,要确保每个worker的随机种子不同,否则会得到相同的结果。
7.3 结果可视化:用热力图展示策略演化过程
Matlab的imagesc函数非常适合展示策略演化。我把每轮每个智能体的策略值存成一个矩阵,行是迭代轮数,列是智能体编号,然后用热力图显示。收敛快的算法会很快出现稳定的色带,震荡的算法则呈现花斑状。
figure; imagesc(history); colorbar; xlabel('智能体编号'); ylabel('迭代轮数'); title('策略演化热力图');这个图在论文里也很出彩,审稿人一眼就能看出算法的收敛行为。
7.4 代码版本管理:Matlab项目的Git实践
Matlab项目用Git管理时,.mat数据文件和.fig图片文件不要提交,只提交.m和.mlx文件。在项目根目录放一个.gitignore:
*.mat *.fig *.asv slprj/另外,Matlab的实时脚本.mlx是二进制格式,Git无法做行级diff。如果团队协作,建议用纯.m文件写代码,把文档和说明放在单独的Markdown文件里。
8. 从离散均衡到在线学习:这个框架还能怎么扩展
离散纳什均衡寻优的框架并不局限于静态博弈。我最近在尝试把它扩展到重复博弈场景:智能体不是一次性选择策略,而是反复交互,每轮观测到历史收益后再做决策。这种情况下,均衡的概念从纳什均衡扩展到了子博弈完美均衡和演化稳定策略。
另一个扩展方向是不完全信息博弈。实际工程中,智能体往往不知道其他智能体的收益函数,只能通过观测到的策略和收益来推断。这需要引入贝叶斯博弈的框架,用概率分布来表示对其他智能体类型的信念,然后求解贝叶斯纳什均衡。Matlab的统计工具箱提供了贝叶斯推断的函数,可以在此基础上搭建。
还有一个我比较看好的方向是多智能体强化学习与离散均衡的结合。把每个智能体建模为一个Q-learning智能体,状态是其他智能体的策略组合,动作是自己的策略选择,收益就是博弈的收益函数。当所有Q表收敛时,对应的策略组合就是纳什均衡。这种方法的优势在于不需要知道博弈的结构,完全数据驱动。我在一个小规模场景下试过,收敛速度比分布式随机逼近慢,但对博弈结构的假设更少,适应性更强。
如果你正在做相关的研究或工程落地,建议先从势博弈入手,把最佳响应和分布式随机逼近都实现一遍,对比它们在具体场景下的表现。势博弈的代码量不大,但能帮你把整个框架跑通,后续扩展到更复杂的博弈类型时就有了一个可靠的基准。