ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MATLAB实现量子近似优化算法:组合优化与最大割问题详解

MATLAB实现量子近似优化算法:组合优化与最大割问题详解

简介:面向需要求解旅行商、背包、图着色等组合优化问题的技术人员与科研人员,这份PDF文档讲解如何基于MATLAB实现量子近似优化算法(QAOA)。资源共1个文件,文件类型为PDF,资源包约4.01MB,文档支持目录章节跳转、阅读器左侧大纲显示和章节快速定位,所有文字、图表、函数与目录均显示正常。内容从量子比特、量子门、量子纠缠等基础回顾入手,系统介绍QAOA的基本思想、问题哈密顿量与驱动哈密顿量的构建、量子态初始化与交替演化、参数优化及测量取解流程,并针对最大割问题给出完整MATLAB实现示例,包含数学建模、代码实现、测量与结果分析。文档同时涵盖MATLAB环境搭建、Optimization Toolbox与Quantum Computing Toolbox的安装配置,以及算法性能评估指标(近似比、收敛速度、计算时间)和多种优化策略,结构上先原理后实践,章节层级清晰,适合按需查阅。已有65人浏览学习,可配合MATLAB动手复现最大割问题求解流程,亦可作为算法课程设计、毕业设计或量子计算入门参考。资源仅供学习参考,请勿用作商业用途。

1. 组合优化问题遇到量子近似优化算法:一份MATLAB实现的完整PDF值不值得下

组合优化问题在物流调度、电路布局、资源分配等领域几乎是每天都绕不开的硬骨头,难点不在于问题本身难理解,而在于解空间会随规模指数膨胀:n个二值变量的候选组合就有2^n种,靠传统遍历思路找最优解,十几个变量就能把算力拖到墙角。量子近似优化算法(QAOA)的核心思路是,把目标函数编码成量子哈密顿量,利用量子叠加与纠缠特性做交替演化,再由经典优化器调整参数逼近近似最优解。而MATLAB凭借矩阵运算、优化工具箱和高可读性的脚本环境,很适合把这套算法从公式落地成能跑的代码。这份PDF把原理、环境配置、函数实现和最大割案例串成了一条完整链路,适合想跑通QAOA但不想从零啃量子计算的开发者、研究者。

2. QAOA原理拆解:问题哈密顿量、驱动哈密顿量与参数演化的三角关系

2.1 组合优化问题的数学本质:离散、约束与指数爆炸

先看组合优化问题的标准形式。给定决策变量集合x和可行域,目标函数f(x)需要最小化或最大化,同时要满足一组不等式约束g_i(x)≤0和等式约束h_j(x)=0,而x的取值落在有限的离散集合中。这三个特征放在一起,就注定了这类问题的求解复杂度往往是指数级。

TSP问题中,解空间是所有城市的排列,规模是n!;背包问题的解空间是全部“选/不选”组合,规模是2^n;图着色问题的候选方案更是达到k^n级别。这三个例子在文档第二章里都有,而且每个都对应一个真实工业场景——TSP对应物流配送路径规划,背包问题对应企业预算分配,图着色对应通信网络频率分配。这说明组合优化不是纯理论玩具,它直接决定了一批工程系统的运行效率。

传统求解方式的边界在文档里也被坦白地讲了。枚举法能保证最优,但只在n很小的时候可行;贪心算法速度快,却因为只看局部最优而常常与全局最优擦肩而过;动态规划虽然理论上漂亮,但需要问题具备最优子结构和重叠子问题两个性质,真实问题里能同时满足这两个性质的比想象中少得多。把这些方法摊开看,本质上都是经典计算框架下的折中:要么牺牲质量换速度,要么牺牲速度保质量,很难两全。这一认知是理解QAOA为什么值得尝试的前提——它在多项式时间内给出一个带概率保证的近似解,用略微放宽的“最优”换取“算得动”。

2.2 问题哈密顿量:把目标函数翻译成矩阵本征值

QAOA的第一个关键步骤,是把一个目标函数转换成矩阵形式。对于二值变量x_i∈{0,1},可以通过x_i=(I-Z_i)/2与Pauli-Z矩阵建立等价关系。Z矩阵是量子计算里最基础的矩阵之一:

Z = [1 0; 0 -1]

当x_i=0时,我们有向量|0⟩,Z作用在|0⟩上本征值为+1,所以(I-Z)/2作用结果为0;当x_i=1时本征值为-1,作用结果为1。这样一来,任何由二值变量构成的目标函数都可以展开为若干Z矩阵的线性组合。

文档给的例子是f(x)=x₁+x₂,映射后的哈密顿量为H_P=½(I-Z₁)+½(I-Z₂)。这里的Z₁和Z₂并不是简单的2×2矩阵,而是分别被Kronecker积扩展到了4×4的完整空间。具体来说,Z₁=kron(Z,I),Z₂=kron(I,Z)。验证这个映射正确与否的办法很简单:把四个计算基态|00⟩、|01⟩、|10⟩、|11⟩分别代入,四个本征值正好是目标函数的0、1、1、2。这个“本征值即目标函数值”的特性,是后续所有量子演化的根基。

约束条件的处理则要绕一些。文档的方法是引入惩罚项:对约束g(x)≤0,在目标函数里加C·max(0,g(x))。算法优化时,违反约束的解会因为能量偏高而被规避,满足约束的解则不影响原始目标。C的取值不能太大也不能太小:太大导致数值稳定性下降,太小则约束形同虚设。我一般会让C是目标函数最大量级的5到10倍,再根据结果微调。

2.3 驱动哈密顿量:量子态在解空间中“走动”的引擎

如果只有问题哈密顿量就够了,那QAOA就退化成单纯的量子绝热演化。但QAOA不是绝热算法,它的核心是用另一个哈密顿量来做“驱动”,让量子态不至于锁死在某一个计算基态上。

驱动哈密顿量最常用的形式是H_D=ΣX_i,X是Pauli-X矩阵:

X = [0 1; 1 0]

X门的作用是翻转量子比特:把|0⟩变|1⟩,把|1⟩变|0⟩。多个X_i叠加后的效果,相当于给整个量子态提供一个在所有计算基之间跃迁的“动能”。在能量景观中,量子态既能下降(受问题哈密顿量引导),又能横向移动(受驱动哈密顿量引导),两者交替作用才有机会跳出局部极小。

文档特别提到,驱动哈密顿量的选择并非唯一。理论上只要能让量子态遍历解空间的哈密顿量都可以,但X_i求和是最基础和硬件友好度最高的选择。如果换成其他驱动形式,比如带权重的X组合,可能会提升特定问题的表现,但也可能让电路实现复杂度和参数优化难度同步上升。模拟阶段建议先用标准形式跑通,再考虑变种。

2.4 p层交替演化:经典优化器与量子回路如何协作

QAOA的演化结构可以看作是一个固定模板。先从所有量子比特均为|+⟩态的初始状态出发,|+⟩=(|0⟩+|1⟩)/√2,然后交替作用两组幺正算子:

  • 问题哈密顿量演化:e^{-iγ_k H_P}
  • 驱动哈密顿量演化:e^{-iβ_k H_D}

重复p轮。每一轮由γ_k、β_k两个参数控制,总共2p个参数。量子态在这2p个参数的影响下,会在解空间里形成特定的概率分布。参数选得好,高概率塌缩到近似最优解;参数选不好,结果和随机猜测没什么区别。

参数优化的标准做法是计算演化后量子态在问题哈密顿量下的期望值E=⟨ψ|H_P|ψ⟩,然后用经典优化器去找让E最小的那组参数。文档选用fminsearch,也就是Nelder-Mead单纯形法。这种算法不用梯度信息,对无解析梯度的问题非常省事,但缺点是只能收敛到局部极小,初始参数的选择直接决定最终解的质量。p越大,理论上能表达的最优解质量越高,但2p维参数空间的优化难度也在同步上升。模拟阶段p=1到3通常是性价比最高的区间。

3. MATLAB实现核心流程:从哈密顿量构建到完整QAOA闭环

3.1 环境准备:哪些工具箱是刚需

文档在正式代码前专门留了一章讲环境搭建,这个安排挺合理。MATLAB跑QAOA至少需要两样东西:主程序自带的矩阵运算能力(kron、expm、zeros等),以及Optimization Toolbox提供的fminsearch和fmincon。前者是量子态和哈密顿量表示的基础,后者负责参数优化环节。文档提到的Quantum Computing Toolbox则属于可选辅助,它提供的QubitRegister、measure等类可以简化量子电路描述,但对本文这个规模的实现来说,完全可以用手写矩阵替代。

我个人的建议是:主程序加Optimization Toolbox必须装好;量子计算工具箱可装可不装,装了做辅助验证,不装也不影响主流程。用附加功能管理器搜索安装时,注意一定让安装路径和工作路径一致,否则会出现函数能搜到但脚本调用报错的情况。装完在命令窗口执行ver检查工具箱是否出现在列表里,比反复重启MATLAB高效得多。

环境配置层面的工作路径和编辑器设置虽然琐碎,但对后续调试影响很大。建议把所有QAOA脚本放进一个独立目录,然后在主页选项卡里把当前文件夹指过去,避免脚本互相找不到依赖。编辑器缩进改成4空格,多层嵌套循环里能明显减少阅读成本。

3.2 问题哈密顿量构建:Kronecker积与索引错位

问题哈密顿量是整个算法里最容易写错的部分。核心操作是把作用在单个比特上的2×2矩阵,用kron函数一步步扩展到2^n×2^n的完整空间中。以最大割问题为例,考虑一条边(i,j),其哈密顿量贡献为½(I-Z_i Z_j),把所有边累加就得到问题哈密顿量。对应代码:

% 构建最大割问题的问题哈密顿量 H_P % 输入: n - 顶点数量, edges - m x 2 边矩阵 % 输出: H_P - 2^n x 2^n 复数矩阵 function H_P = build_problem_hamiltonian(n, edges) Z = [1 0; 0 -1]; H_P = zeros(2^n, 2^n); for k = 1:size(edges, 1) i = edges(k, 1); j = edges(k, 2); % 扩展Z到第i号比特, 其余比特保持单位阵 Z_i = kron(eye(2^(i-1)), kron(Z, eye(2^(n-i)))); % 扩展Z到第j号比特 Z_j = kron(eye(2^(j-1)), kron(Z, eye(2^(n-j)))); % 累加 0.5*(I - Z_i*Z_j) H_P = H_P + 0.5 * (eye(2^n) - Z_i * Z_j); end end

这里最需要留意的是Z_i和Z_j的维度逻辑。eye(2^(i-1))保证前i-1个比特不被触碰,中间的Z作用在第i个比特,后续eye(2^(n-i))保证剩余比特不受影响。三个矩阵按顺序kron起来,就得到了正确的扩展矩阵。索引一旦从1开始计数时搞错,或者把i和j写反,矩阵维度虽然能对上,但编码出的哈密顿量完全错误。这种错误最常见的暴露方式是:期望值计算正常收敛,但得到的解在经典意义上明显不是最优。我在调试时会在构建完成后打印H_P的对角元,和暴力枚举的目标函数值逐一比对,几秒就能发现问题。

3.3 驱动哈密顿量与初始态:两块固定积木的组装

驱动哈密顿量的实现逻辑与问题哈密顿量一致,只是把Z换成X。它的代码更短:

% 构建驱动哈密顿量 H_D = sum_i X_i % 输入: n - 量子比特数量 % 输出: H_D - 2^n x 2^n 矩阵 function H_D = build_driver_hamiltonian(n) X = [0 1; 1 0]; H_D = zeros(2^n, 2^n); for i = 1:n % 将X扩展到第i个比特 X_i = kron(eye(2^(i-1)), kron(X, eye(2^(n-i)))); H_D = H_D + X_i; end end

初始态则不同,它不是一个矩阵,而是一个列向量。每个量子比特的|+⟩态表示为[1;1]/sqrt(2),n个|+⟩态的Kronecker积就是长度为2^n的全叠加态向量:

% 初始化量子态: n比特全|+>叠加态 % 输入: n - 量子比特数量 % 输出: psi_0 - 长度2^n的列向量 function psi_0 = initialize_state(n) plus = [1; 1] / sqrt(2); psi_0 = 1; for k = 1:n psi_0 = kron(psi_0, plus); end end

这两块代码没有太多参数可调,但它们决定了QAOA的探索能力。全|+⟩态是X基下的均等叠加,表示所有候选解都被平等对待;H_D则保证演化过程能够突破单一计算基的局限。我的经验是,如果初始态里混入了非|+⟩成分,或者H_D里某个X_i漏加,最终的优化曲线一般会表现出“怎么调参都不下降”的异常,优先查这两处。

3.4 量子态演化与参数优化:QAOA主循环

演化函数严格按QAOA模板实现。顺序是先作用问题哈密顿量,再作用驱动哈密顿量,不能反过来。因为e^{-iγ_k H_P}和e^{-iβ_k H_D}一般不对易,顺序颠倒会导致完全不同的量子态分布,而且结果通常更差:

% QAOA量子态演化 % 输入: psi_0 - 初始态, H_P/H_D - 两个哈密顿量, beta/gamma - 长度p的参数向量 % 输出: psi - 演化后量子态 function psi = evolve_state(psi_0, H_P, H_D, beta, gamma) p = length(beta); psi = psi_0; for k = 1:p U_P = expm(-1i * gamma(k) * H_P); % 问题哈密顿量演化算子 U_D = expm(-1i * beta(k) * H_D); % 驱动哈密顿量演化算子 psi = U_D * U_P * psi; end end

expm是矩阵指数函数,和逐元素的exp完全是两回事。这里用1i而不是i,是因为在MATLAB里i可能被用户变量覆盖,1i作为内置虚数单位更稳。参数优化目标函数的核心是计算期望值:

% 优化QAOA参数, 使期望值 <psi|H_P|psi> 最小 function [beta_opt, gamma_opt, min_cost] = optimize_parameters(n, edges, p) H_P = build_problem_hamiltonian(n, edges); H_D = build_driver_hamiltonian(n); psi_0 = initialize_state(n); % 目标函数只接收参数向量, 内部重建beta/gamma并演化 objective = @(params) evaluate_objective(params, psi_0, H_P, H_D, p); initial_params = rand(2 * p, 1); % 随机初值, 实际建议固定种子 [params_opt, min_cost] = fminsearch(objective, initial_params); beta_opt = params_opt(1:p); gamma_opt = params_opt(p + 1:end); end function cost = evaluate_objective(params, psi_0, H_P, H_D, p) beta = params(1:p); gamma = params(p + 1:end); psi = evolve_state(psi_0, H_P, H_D, beta, gamma); cost = real(psi' * H_P * psi); % 期望值取实部 end

fminsearch本身是无约束优化,所以beta和gamma理论上没有上下限。但实践中gamma过大或过小会让expm的数值行为变差,我一般会把参数缩放到[-π, π]区间再交给优化器,能减少一些浮点病态的触发。整体上这个函数把QAOA外循环完整封装了,调用方只需要提供n、edges和p三个参数。

提示:fminsearch是局部优化器,初值选择比算法本身更容易影响结果,正式实验前务必固定随机种子,后面避坑章会展开说。

3.5 测量与结果分析:从概率分布到二进制答案

演化完成后,量子态向量psi的每个分量的模平方,就是对应计算基态被测量到的概率。测量就是按这个概率分布采样,多次采样后统计出现次数最多的那个二进制串作为近似最优解:

% 测量量子态并返回出现频率最高的二进制状态 % 输入: psi - 演化后量子态, num_measurements - 采样次数 % 输出: result - n位二进制字符串 function result = measure_and_analyze(psi, num_measurements) n = log2(length(psi)); % 反推比特数 probs = abs(psi).^2; % 模平方得到测量概率 probs = probs / sum(probs); % 归一化, 消除浮点误差 cdf = cumsum(probs); % 累积分布 sampled = zeros(1, num_measurements); for idx = 1:num_measurements r = rand(); sampled(idx) = find(cdf >= r, 1, 'first'); end counts = histcounts(sampled, 1:length(psi)+1); [~, max_idx] = max(counts); result = dec2bin(max_idx - 1, n); end

归一化那行不是多余操作。expm的数值误差会让模平方和偏离1,不归一化直接采样的后果是累积分布函数的末端出现空洞,高端状态被系统漏采。dec2bin的输出是字符数组,后续计算目标函数时用result-'0'把它转成数值向量。如果测量得到的二进制串在目标函数计算中表现异常,先看这个转换有没有出错。

4. 避坑指南:MATLAB跑QAOA的五条血泪经验

4.1 现象:n刚过12,构建哈密顿量直接内存溢出

现象:把顶点数从10调到14,脚本运行到zeros(2^n,2^n)就报“内存不足”,MATLAB直接卡住。

原因:n个量子比特的状态向量长度是2^n,哈密顿量矩阵是2^n×2^n。n=14时,矩阵元素约2.68亿个,复数double每个占16字节,单个矩阵就需要约4.3GB内存,普通开发机根本扛不住。

解决:把n控制在12以内是模拟阶段最务实的方案。超过这个规模,我一般会改用稀疏矩阵表示H_P,或者把H_P按边拆成多个Pauli项,逐项用稀疏矩阵乘法作用于态向量,避免一次性构造大而全的稠密矩阵。这个改动对函数接口没有影响,但内存占用会从指数级降到多项式级。

4.2 现象:expm越跑越慢,优化过程像卡死一样

现象:p=2时还很快,p加到5之后单次参数优化要跑几个小时,进度条纹丝不动。

原因:expm做的是矩阵指数运算,内部要走特征分解或者Padé逼近加缩放平方,复杂度远超普通矩阵乘法,并且在维度增大时呈超线性增长。QAOA每层演化要调用两次expm,优化器几百次评估下来时间全耗在这里。

解决:提前把每个gamma、beta对应的U_P、U_D计算并缓存起来,同一个参数在优化中重复出现时直接取缓存;或者换成稀疏expm路径,MATLAB的expm支持稀疏输入但需要先转换数据类型。另一种折中是限制p,p=2到3通常就够教学级验证了,强行上高p只会让优化器在巨大参数空间里迷路。

4.3 现象:同一个问题两次运行,结果完全对不上

现象:用rand随机初值跑fminsearch,第一次近似比0.92,第二次掉到0.71,代码没改过。

原因:QAOA的目标函数是多峰且无解析梯度的,fminsearch作为无导数局部搜索器,初值落在哪个吸引域就收敛到哪个局部极小。随机种子不固定,等价于每次从不同起点爬山,结果自然像抽签。

解决:在脚本开头固定随机种子rng(0),是最低成本的复现保障。我一般还会加一道预处理:把beta和gamma在[-π,π]上做均匀网格扫描,取期望值最低的网格点作为fminsearch初值。网格粗一点没关系,只要把优化器推进到正确的吸引域,之后收敛就稳定很多。

4.4 现象:测量结果总是全0状态,像被“钉死”了一样

现象:n=6的小图,采样1000次里900多次都是|000000⟩,得到的解在经典上明显不是最优。

原因:两种常见可能。一是某层gamma落在让问题哈密顿量过强的区间,量子态被挤压到单一低能态附近,概率分布严重偏斜;二是概率未归一化直接采样,累积分布末端空心导致高比特态被系统性丢弃。

解决:先检查sum(abs(psi).^2)是否接近1,不接近就先归一化;再看参数是否收敛到合理区间。单态概率超过0.95时,可以减小对应层的gamma绝对值,或增加采样次数到5000以上。如果还是单态垄断,重新优化参数通常能缓解。

4.5 现象:文档里的QubitRegister类,在自己电脑上报未定义

现象:照着文档敲QubitRegister(2),命令窗口报“未定义函数或变量”,hadamard、cnot也找不到。

原因:Quantum Computing Toolbox在MATLAB不同版本里API差异很大,部分旧版本根本没有这个类,或者类名和方法已经改名。

解决:绕开专用工具箱,用手写矩阵实现。QAOA里用到的Hadamard门就是[1 1;1 -1]/sqrt(2)这个2×2矩阵,完全不需要调用任何专用函数。这也是我为什么在前文建议把量子计算工具箱当辅助而不是依赖——核心流程足够简单,矩阵运算完全能覆盖。

5. 最大割案例跑通:从图定义到近似解的完整闭环

5.1 问题建模:无向图、二值变量与目标函数

最大割问题在QAOA相关文献里几乎是最常见的演示平台。给定无向图G=(V,E),目标是把顶点划分成S1和S2两个集合,使得连接两个集合的边数最大化。用二值变量x_i标记归属,x_i=0表示顶点在S1,x_i=1表示在S2。对边(i,j),当两端归属不同时贡献为1,相同则为0,表达式为:

C(x)=Σ_{(i,j)∈E}(x_i(1-x_j)+x_j(1-x_i))

文档的第六章拿4节点环形图做案例,顶点1-2-3-4-1依次相连。这个例子规模小,暴力枚举只有2^4=16种划分,非常方便验证算法的正确性。最大割的最优划分是{1,3}和{2,4},割边数为2。事实上,有经验的读者一眼就能看出最优解,所以这个案例真正的价值不在“求解本身”,而在把QAOA的各个函数完整串起来的教学意义。

5.2 完整代码串联:一个脚本跑完QAOA全流程

把第3章的函数放到同一个路径下,再写一个主脚本,就是一个可以直接运行的完整案例:

% 最大割问题: 4节点环形图 n = 4; % 顶点数 edges = [1 2; 2 3; 3 4; 4 1]; % 环形四条边 p = 2; % QAOA层数 rng(0); % 固定随机种子, 保证可复现 % 1. 构建两个哈密顿量 H_P = build_problem_hamiltonian(n, edges); H_D = build_driver_hamiltonian(n); % 2. 用fminsearch优化参数 [beta_opt, gamma_opt, min_cost] = optimize_parameters(n, edges, p); % 3. 按最优参数演化量子态 psi_0 = initialize_state(n); psi = evolve_state(psi_0, H_P, H_D, beta_opt, gamma_opt); % 4. 测量得到近似最优解 result = measure_and_analyze(psi, 1000); fprintf('近似最优解(二进制): %s\n', result); % 5. 计算该解对应的割边数, 验证解质量 x = result - '0'; % 字符数组转数值向量 cut_count = 0; for k = 1:size(edges, 1) i = edges(k, 1); j = edges(k, 2); cut_count = cut_count + x(i)*(1-x(j)) + x(j)*(1-x(i)); end fprintf('最大割边数: %d\n', cut_count);

第5步是整个主脚本里最容易省略但恰恰最该保留的部分。QAOA输出的是一串二进制字符,如果不把它翻译回目标函数值,你就永远不知道算法到底找到了什么质量的解。文档的案例分析部分把“结果分析”列为单独小节,就是这个道理。割边数计算公式必须和建模部分保持一致,否则会出现解正确但评估错误的情况,白白浪费一次成功运行。

5.3 结果讨论:近似比怎么算,p层数怎么选

割边数的绝对数字意义不大,看的是相对最优解的差距。4节点环形图的最优割边数为2,如果QAOA给出的解割了2条边,近似比就是2/2=1.0;如果只割了1条边,近似比就是0.5。近似比这个指标在文档第七章有专门定义,它是衡量QAOA解质量的第一标准。

p层数对该图的影响可以做一个快速实验:p=1时,QAOA演化后量子态的能力有限,测得最优解的概率通常不高;p=2时,参数空间变大,fminsearch能找到更精准的参数组合,几乎每次都能返回割2条边的解。但在p=3以上,优化时间成倍增加,解质量的提升却趋于平缓。这个现象不是4节点环图独有的,在更大的随机图上同样会出现,原因是QAOA的表达能力随p提升,但经典优化器在更高维参数空间里找到全局极小的难度也在同步增大。

实际操作中,我有一个习惯:先从p=1开始跑通整个链路,确认测量结果符合预期,再逐步加大p看近似比和耗时的变化。这样每一步都能定位到具体环节的问题,不会在p增大后把所有时间开销都归咎于量子演化。

6. 进阶验证技巧:用暴力枚举给QAOA结果当裁判

QAOA实现完后,最常被问的问题是“怎么知道算法没写错”。我的做法很朴素:对n不超过15的问题,直接用暴力枚举算出全局最优值,再和QAOA输出做对比,误差一目了然。这个方法不依赖任何额外工具箱,而且能把问题局限在能看透的小规模上,是性价比最高的验证手段。

% 暴力枚举最优值, 作为QAOA的参照基线 best_cost = 0; % 最大割问题, 从0开始 for state = 0:(2^n - 1) x = dec2bin(state, n) - '0'; cost = 0; for k = 1:size(edges, 1) i = edges(k, 1); j = edges(k, 2); cost = cost + x(i)*(1-x(j)) + x(j)*(1-x(i)); end best_cost = max(best_cost, cost); % 最大化割边数 end fprintf('暴力枚举最优值: %d\n', best_cost);

把这个脚本和QAOA主脚本放在同一个目录,先跑暴力枚举得到best_cost,再跑QAOA得到割边数。两者相除就是近似比。如果近似比稳定在1.0附近,说明哈密顿量构建和演化逻辑大概率没有问题;如果偏差超过10%,优先检查H_P对角线元素与暴力枚举目标函数值的映射关系。这个方法帮我抓住过好几次索引偏移错误,这类错误在单纯看测量结果时很难暴露,因为QAOA解并不总是最优解,偶尔差一点会让人误以为是算法精度问题,而不是实现bug。

从那以后我每次复现QAOA相关代码或设计方案,都会先让暴力枚举做裁判,确认基线之后再谈优化技巧和性能调优。小规模问题上的正确性验证虽然不解决扩展性问题,但能把“算法本身的问题”和“实现过程的问题”干净地切开,这份PDF里的完整代码路径配合这个验证习惯,恰好形成一套能自洽的运行闭环。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进