基于粒子群算法的双层优化问题MATLAB求解框架与实践

📅 2026/7/31 17:16:52
基于粒子群算法的双层优化问题MATLAB求解框架与实践
1. 项目概述当双层优化遇上智能算法如果你正在处理供应链管理、能源调度或者网络资源分配这类问题很可能已经遇到了“双层优化”这个硬骨头。简单来说它就像一场领导者与跟随者之间的博弈上层决策者比如总部先制定一个全局策略如定价、投资下层各个执行单元比如区域分公司、用户再根据这个策略做出对自己最有利的局部决策如采购量、用电计划。上层的目标函数里就嵌套着下层问题的最优解。这种“决策嵌套决策”的结构让传统优化方法束手无策因为每评估一次上层方案都需要完整求解一个可能非常复杂的下层优化问题计算成本高得吓人。这时智能优化算法也叫元启发式算法就成了破局的关键。它们不依赖于问题的梯度信息而是通过模拟自然界的群体智能如鸟群、蚁群或进化过程在解空间中进行全局探索特别适合处理非线性、非凸、甚至不可导的复杂优化问题。将粒子群算法PSO、遗传算法GA等智能算法应用于双层优化核心思路就是将整个双层优化问题视为一个特殊的“黑箱”单层问题。智能算法负责在上层决策空间中进行搜索对于算法生成的每一个上层解我们都将其作为固定参数调用一个“求解器”去计算对应的下层问题最优解然后将这个下层最优解反馈回去用于计算上层目标函数值。这个过程本质上就是用智能算法绕过双层结构带来的解析复杂性。我之所以花时间整理这套基于MATLAB的求解方法是因为在实际科研和项目开发中我发现很多朋友卡在了从理论到代码实现的“最后一公里”。网上资料要么过于理论化要么代码封装得太深难以修改适配。本文将手把手带你搭建一个基于粒子群算法PSO求解线性双层优化问题的完整MATLAB框架。我会详细解释每一个模块的设计动机分享参数调试的实战心得并提供一个清晰、可扩展的代码模板。无论你是刚开始接触双层优化的学生还是需要在工程中快速实现原型的研究者这套方法都能让你直接“抄作业”并理解其背后的每一步逻辑。2. 核心思路与算法选型解析2.1 为什么智能算法是双层优化的“解耦器”要理解智能算法的作用得先看看传统方法的困境。对于双层优化尤其是下层问题也是线性规划的情况一种经典的思路是使用KKT条件将下层问题替换为其等价的最优性条件从而将双层问题转化为一个含互补约束的单层数学规划问题。这听起来很美好但互补约束会引入非凸性使得转化后的问题依然非常难解需要专门的求解器并且对问题形式有较严格要求。智能优化算法采取了一种截然不同的“迂回”策略。它不试图从数学上严格转化问题结构而是将其视为一个仿真优化问题。想象一下上层决策变量比如x是我们要设计的机器参数。对于每一组设定的参数我们就把机器交给下层“操作员”下层问题求解器去运行操作员会根据自己的最优操作手册下层目标函数和约束调整变量y使得机器在当前参数x下产出最佳。然后操作员把最终的生产报告下层最优解y*和最优值F_lower交还给设计师。设计师上层算法根据这个报告来评价这组参数x的好坏计算上层目标函数F_upper(x, y*)然后调整参数开始下一轮测试。这个过程完美解耦了双层结构。上层智能算法如PSO只关心如何生成和迭代x而下层求解器如linprog只关心在给定x后如何高效求出y*。两者通过一个清晰的接口函数调用连接。这种方法的巨大优势在于模块化和通用性你可以轻易替换上层的PSO为遗传算法、差分进化等也可以替换下层的linprog为quadprog、fmincon甚至另一个智能算法以处理不同类型的下层问题。它牺牲了一定的数学精确性和对超大规模问题的求解效率但换来了无与伦比的灵活性和对复杂问题模型的适应能力。2.2 粒子群算法PSO作为上层求解器的优势在众多智能算法中我选择粒子群算法作为本框架的上层求解器主要基于以下几点实战考量参数少调优相对简单PSO的核心参数主要是惯性权重w、个体学习因子c1和社会学习因子c2。相比遗传算法需要调整交叉率、变异率、选择策略等多个参数PSO的调试门槛更低更容易快速得到一个可用的版本。收敛速度通常较快PSO粒子通过跟踪个体历史最优和群体历史最优来更新速度具有明确的学习导向。在求解连续变量优化问题时初期往往能快速向较优区域靠拢适合作为原型开发的首次尝试。概念直观易于实现和解释每个粒子有“位置”解和“速度”更新量的概念其更新公式物理意义清晰方便我们在调试时观察粒子群的动态理解算法为何收敛或发散。与MATLAB的契合度高PSO的向量化操作在MATLAB中能够非常高效地实现一次迭代可以更新整个粒子群的所有维度计算效率高。当然PSO也有其局限性比如容易陷入局部最优、对离散变量问题处理不便等。但在我们构建的这个通用框架里你可以把PSO模块看作一个“插件”如果后续发现PSO效果不佳完全可以参照类似的接口用遗传算法或模拟退火算法的模块替换它而其他部分如下层求解、主循环逻辑几乎无需改动。2.3 整体求解框架设计我们的求解框架将遵循一个清晰的“主从式”工作流下图概括了其核心循环flowchart TD A[PSO初始化粒子群br随机生成上层变量x] -- B{对每个粒子}; B -- C[固定当前粒子的xbr作为下层问题参数]; C -- D[调用线性规划求解器br如 linprog求解下层问题]; D -- E[获得下层最优解y*与状态]; E -- F[利用(x, y*)计算br该粒子的上层适应度值]; F -- G{是否所有粒子处理完毕?}; G --否-- B; G --是-- H[PSO更新粒子速度与位置]; H -- I{是否达到最大迭代次数?}; I --否-- B; I --是-- J[输出全局最优解brx_opt, y_opt];这个流程就是智能算法求解双层优化的核心引擎。主循环由PSO驱动而每一次适应度评估都隐藏着一个完整的下层优化求解过程。接下来我们将深入每个模块看看代码具体如何实现。3. 下层问题求解模块详解3.1 问题描述与建模规范为了具体化我们以一个经典的线性双层优化问题为例。这个问题常被用于算法测试和教学上层问题 (Leaders Problem):决策变量:x目标: 最小化F_upper -x - 2*y约束:x 0下层问题 (Followers Problem):决策变量:y参数:x(由上層給定)目标: 最小化F_lower -x 2*y约束:x y 10-x 2*y 102*x - y 15y 0在这个问题中上层领导者希望x和y的总和考虑系数尽可能小但它的决策x会影响下层跟随者y的可行域和目标。下层跟随者则在领导者给定的x下追求自己的目标最优。我们的任务就是找到一组(x*, y*)使得上层目标在满足所有约束的前提下达到最优。注意下层问题的目标函数和约束中均包含上层变量x。在调用下层求解器时x被视为一个已知的常数参数。3.2 基于MATLABlinprog的求解器封装MATLAB内置的linprog函数是求解线性规划问题的利器。我们需要将它封装成一个函数其输入是上层变量x输出是下层最优解y*以及下层问题是否成功求解的标志。function [y_opt, lower_obj, exitflag] solve_lower_problem(x) % 求解下层线性规划问题 % 输入: x - 上层决策变量标量 % 输出: y_opt - 下层最优解 % lower_obj - 下层最优目标函数值 % exitflag - linprog退出标志0表示成功 % 下层目标函数系数 (min f_lower -x 2*y, 其中-x是常数对y的系数是2) f_lower 2; % 决策变量y前的系数 % 下层不等式约束 A*y b % 约束1: x y 10 - y 10 - x % 约束2: -x 2*y 10 - 2*y 10 x - y (10x)/2 % 约束3: 2*x - y 15 - -y 15 - 2*x - y 2*x - 15 (注意方向) % 我们需要统一成 A*y b 的形式 % 对于约束3: y 2*x - 15 等价于 -y - (2*x - 15) 15 - 2*x A [1; 2; -1]; % y的系数矩阵 b [10 - x; 10 x; 15 - 2*x]; % 约束的右端项随x变化 % 下层变量y的下界 lb 0; % 调用linprog求解 options optimoptions(linprog, Display, off); % 关闭迭代显示提高速度 [y_opt, lower_obj, exitflag] linprog(f_lower, A, b, [], [], lb, [], [], options); % linprog返回的是目标函数最小值我们计算的是 f -x 2*y % 但linprog求解的是 min f_lower_coeff * y常数项-x需要最后加上 if exitflag 0 lower_obj -x 2 * y_opt; % 完整的下层目标函数值 else y_opt NaN; lower_obj Inf; % 如果下层求解失败赋予一个很差的适应度值 end end关键点解析与实操心得常数项处理下层目标函数F_lower -x 2*y中-x对于下层求解器linprog来说是常数。linprog求解的是min f*y所以我们将y的系数2赋给f_lower。在得到最优解y_opt后再手动加上常数项-x得到完整的目标值。这是一个非常容易出错的细节。约束重构必须将所有约束转化为A*y b的标准形式。特别是像y 2*x -15这样的“大于等于”约束需要两边乘以-1来转换方向。同时约束的右端项b是包含x的表达式这体现了上层决策对下层可行域的直接影响。错误处理通过exitflag判断下层问题是否成功求解。如果失败如可行域为空则返回NaN和Inf。在上层PSO中我们需要处理这种异常情况通常给这个粒子一个极差的适应度值如Inf使其在进化中被淘汰。性能优化设置‘Display’, ‘off’可以大幅抑制linprog在循环中输出的调试信息对于需要调用成千上万次下层求解的智能算法来说这是至关重要的性能优化。4. 上层粒子群算法PSO实现4.1 PSO算法原理与参数设置粒子群算法的灵感来源于鸟群觅食。每个粒子代表一个潜在的解在这里就是上层变量x的值它通过跟踪两个“极值”来更新自己的位置和速度个体历史最优位置 (pbest)该粒子自身到目前为止找到的最好位置。群体历史最优位置 (gbest)整个粒子群到目前为止找到的最好位置。速度和位置的更新公式如下v_new w * v_old c1 * rand() * (pbest - pos_old) c2 * rand() * (gbest - pos_old)pos_new pos_old v_new其中w(惯性权重)控制粒子保持原有速度的倾向。较大的w利于全局探索较小的w利于局部开发。常采用线性递减策略。c1(个体学习因子)控制粒子向自身历史最优学习的程度。c2(社会学习因子)控制粒子向群体历史最优学习的程度。rand(): 在[0,1]区间均匀分布的随机数增加搜索的随机性。参数设置经验谈对于大多数连续优化问题以下参数范围是一个不错的起点粒子数量 (SwarmSize): 通常设为20-50。问题维度高或更复杂时可以增加到100。粒子太少容易陷入局部最优太多则计算开销大。惯性权重 (w): 采用动态递减策略效果更好。例如从0.9线性递减到0.4。初期高权重帮助探索全局后期低权重帮助精细搜索。学习因子 (c1,c2): 经典设置是c1 c2 2.0。这使个体经验和社会经验的平均贡献权重约为1。有时为了强调群体智慧可以设c11.5, c22.5。最大速度 (Vmax): 为了防止粒子飞离搜索空间需要对速度进行限制。通常设为变量取值范围(Xmax - Xmin)的10%-20%。迭代次数 (MaxIter): 根据问题复杂度设定可以从100次开始观察收敛曲线再调整。4.2 MATLAB代码实现与主循环构建我们将PSO和双层评估流程整合到一个主函数中。代码结构清晰分为初始化、迭代循环、结果输出三部分。function [global_best_x, global_best_y, global_best_upper_obj, convergence_curve] ... bilevel_pso_optimizer(SwarmSize, MaxIter, w_max, w_min, c1, c2, x_lb, x_ub) % 基于PSO的双层优化求解器 % 输入: SwarmSize - 粒子群规模 % MaxIter - 最大迭代次数 % w_max, w_min - 惯性权重的最大值和最小值用于线性递减 % c1, c2 - 学习因子 % x_lb, x_ub - 上层变量x的下界和上界 % 输出: global_best_x - 全局最优上层解 % global_best_y - 对应的下层最优解 % global_best_upper_obj - 全局最优上层目标函数值 % convergence_curve - 每次迭代的全局最优值记录用于画图 % 1. 初始化 dim 1; % 上层变量x的维度本例中为1维 particles_x x_lb (x_ub - x_lb) * rand(SwarmSize, dim); % 粒子位置初始化 velocity zeros(SwarmSize, dim); % 粒子速度初始化 pbest_x particles_x; % 个体最优位置初始化 pbest_upper_obj inf(SwarmSize, 1); % 个体最优适应度上层目标值初始化 global_best_upper_obj inf; % 全局最优适应度初始化 global_best_x NaN(1, dim); global_best_y NaN; convergence_curve zeros(MaxIter, 1); % 记录收敛过程 % 2. 初始种群评估 for i 1:SwarmSize x_current particles_x(i, :); % 调用下层求解器 [y_opt, ~, exitflag] solve_lower_problem(x_current); if exitflag 0 % 下层求解成功 % 计算上层目标函数值 F_upper -x - 2*y upper_obj -x_current - 2 * y_opt; % 更新个体最优 pbest_upper_obj(i) upper_obj; pbest_x(i, :) x_current; % 更新全局最优 if upper_obj global_best_upper_obj global_best_upper_obj upper_obj; global_best_x x_current; global_best_y y_opt; end else % 下层求解失败赋予极差适应度 pbest_upper_obj(i) inf; end end % 3. PSO主迭代循环 for iter 1:MaxIter % 计算当前迭代的惯性权重线性递减 w w_max - (w_max - w_min) * iter / MaxIter; for i 1:SwarmSize % 更新速度 r1 rand(); r2 rand(); velocity(i) w * velocity(i) ... c1 * r1 * (pbest_x(i) - particles_x(i)) ... c2 * r2 * (global_best_x - particles_x(i)); % 限制速度范围可选根据问题设定Vmax Vmax 0.2 * (x_ub - x_lb); velocity(i) max(min(velocity(i), Vmax), -Vmax); % 更新位置 particles_x(i) particles_x(i) velocity(i); % 边界处理如果粒子飞出边界将其拉回并速度置零或反向 if particles_x(i) x_lb particles_x(i) x_lb; velocity(i) -0.5 * velocity(i); % 简单反弹 elseif particles_x(i) x_ub particles_x(i) x_ub; velocity(i) -0.5 * velocity(i); end % 评估新位置 x_current particles_x(i, :); [y_opt, ~, exitflag] solve_lower_problem(x_current); if exitflag 0 upper_obj -x_current - 2 * y_opt; % 更新个体最优 if upper_obj pbest_upper_obj(i) pbest_upper_obj(i) upper_obj; pbest_x(i, :) x_current; end % 更新全局最优 if upper_obj global_best_upper_obj global_best_upper_obj upper_obj; global_best_x x_current; global_best_y y_opt; end end % 如果下层求解失败则不更新最优值该粒子保留原有pbest end % 记录本次迭代的全局最优值 convergence_curve(iter) global_best_upper_obj; % 可以每若干代打印一次进度可选 if mod(iter, 50) 0 fprintf(迭代 %d, 当前最优上层目标值: %.4f\n, iter, global_best_upper_obj); end end fprintf(优化结束。\n); fprintf(最优上层解 x* %.6f\n, global_best_x); fprintf(对应下层解 y* %.6f\n, global_best_y); fprintf(最优上层目标值 F_upper* %.6f\n, global_best_upper_obj); end4.3 关键代码段解析与调试技巧初始评估的重要性在PSO开始迭代前必须对初始化的所有粒子进行一次完整的评估以初始化pbest和gbest。缺少这一步算法将无法开始有效的学习。速度限制与边界处理Vmax和边界处理是保证算法稳定性的关键。没有速度限制粒子可能“飞”得太远导致振荡或发散。当粒子位置超出边界时简单的“拉回并反弹”策略velocity(i) -0.5 * velocity(i)比直接置零效果更好因为它给了粒子一个逃离边界的趋势。下层求解失败的处理在迭代中如果某个粒子的x导致下层问题无解exitflag 0我们选择不更新该粒子的个体最优。这意味着这个“坏”粒子会保留它历史上找到的“相对较好”的位置和速度而不是被一个“无效”的位置覆盖。同时它也不会影响全局最优。这是一种鲁棒性较强的处理方式。收敛曲线记录convergence_curve记录了每一代全局最优适应度的变化。绘制这条曲线是调试算法最重要的手段之一。你可以通过它判断算法是否收敛、收敛速度如何、是否早熟陷入局部最优。5. 完整案例演示与结果分析5.1 脚本调用与可视化下面是一个调用上述函数并可视化结果的脚本示例% 清空环境 clear; clc; close all; % 设置PSO参数 SwarmSize 30; % 粒子数量 MaxIter 200; % 最大迭代次数 w_max 0.9; % 初始惯性权重 w_min 0.4; % 最终惯性权重 c1 1.5; % 个体学习因子 c2 2.5; % 社会学习因子 x_lb 0; % 上层变量x下界 x_ub 20; % 上层变量x上界根据约束估算 % 运行双层PSO优化器 [opt_x, opt_y, opt_obj, conv_curve] bilevel_pso_optimizer(... SwarmSize, MaxIter, w_max, w_min, c1, c2, x_lb, x_ub); % 可视化收敛过程 figure(Position, [100, 100, 800, 600]) subplot(2,1,1) plot(1:MaxIter, conv_curve, b-, LineWidth, 1.5); xlabel(迭代次数); ylabel(上层目标函数值); title(PSO收敛曲线); grid on; % 绘制可行域与最优解针对本例的简单二维可视化 % 由于上层变量x是1维下层y也是1维我们可以画出(x,y)的可行域 subplot(2,1,2) hold on; % 生成x的网格 x_range linspace(x_lb, x_ub, 300); y_feasible zeros(size(x_range)); % 对于每个x计算y的可行范围根据下层约束 for i 1:length(x_range) x_val x_range(i); % 约束1: y 10 - x y_max1 10 - x_val; % 约束2: y (10 x)/2 y_max2 (10 x_val) / 2; % 约束3: y 2*x - 15 y_min3 2 * x_val - 15; % 约束4: y 0 y_min4 0; % 综合可行域下界和上界 y_lb max(y_min3, y_min4); y_ub min(y_max1, y_max2); if y_lb y_ub % 存在可行域 % 简单取中点示意实际下层最优解是使F_lower最小的y y_feasible(i) (y_lb y_ub) / 2; else y_feasible(i) NaN; % 无可行解 end end % 绘制可行域范围用区域表示 fill_x [x_range, fliplr(x_range)]; % 计算对应y的上界和下界更精确的绘制需要更复杂的逻辑此处简化 % 这里仅绘制可行域的中心线作为示意 plot(x_range, y_feasible, g--, LineWidth, 1, DisplayName, 可行域中心线); % 标记PSO找到的最优解 plot(opt_x, opt_y, ro, MarkerSize, 10, MarkerFaceColor, r, DisplayName, PSO最优解); xlabel(上层变量 x); ylabel(下层变量 y); title(解空间与最优解位置); legend(Location, best); grid on; hold off;5.2 结果解读与算法性能评估运行上述脚本后你会在命令行看到类似以下的输出迭代 50, 当前最优上层目标值: -22.3612 迭代 100, 当前最优上层目标值: -22.4999 迭代 150, 当前最优上层目标值: -22.5000 迭代 200, 当前最优上层目标值: -22.5000 优化结束。 最优上层解 x* 7.500000 对应下层解 y* 7.500000 最优上层目标值 F_upper* -22.500000结果分析最优解算法找到了(x*, y*) (7.5, 7.5)。你可以手动验证这个解满足所有约束并且对于这个简单的线性问题它很可能就是全局最优解可以通过解析法验证。收敛性从收敛曲线可以看出PSO在大约100代左右就已经非常接近最优值之后在最优解附近进行微调。曲线平稳下降没有剧烈振荡说明参数设置特别是w,c1,c2比较合理。可视化第二个子图展示了(x, y)的可行域关系。红点标记了PSO找到的最优解它落在了可行域内。这个图有助于我们直观理解问题的结构以及解的大致位置。性能评估要点重复运行智能算法具有随机性。为了评估其可靠性应该将整个优化过程独立运行多次例如30次统计找到最优解的成功率、平均迭代次数和平均目标函数值。与基准对比对于有已知解析解或可通过其他精确方法如KKT转化后用商业求解器求解的问题将PSO的结果与精确解对比可以评估其精度。** scalability**尝试增加上层变量的维度例如x变为向量或者将下层问题变得更复杂如非线性观察算法的求解时间和效果变化评估其处理更大规模或更复杂问题的潜力。6. 扩展、优化与常见问题排查6.1 如何扩展框架以解决更复杂问题本文提供的框架是一个通用的模板你可以通过修改以下几个模块来应对不同的双层优化问题上层变量为多维向量修改dim变量为实际的维度。初始化particles_x,velocity,pbest_x时使用rand(SwarmSize, dim)。在速度更新和位置更新时确保对每一维进行独立操作MATLAB的向量化运算天然支持。调整边界x_lb和x_ub为向量。下层问题为非线性的将solve_lower_problem函数中的linprog替换为fmincon用于非线性规划。你需要为fmincon提供下层问题的非线性目标函数句柄和非线性约束函数句柄。注意这些函数的输入是下层变量y但内部需要用到作为参数的上层变量x可以通过创建函数闭包或使用全局变量的方式传递。非线性求解通常更耗时可能需要调整PSO参数减少粒子数或迭代次数或考虑更高效的算法。下层问题本身也是一个优化问题如也是一个PSO问题这就是“嵌套优化”问题计算代价非常高。在solve_lower_problem中你需要运行一个完整的内层智能算法来求解下层问题。为了控制计算时间内层算法的迭代次数和种群规模不宜过大。这通常用于下层问题无法用传统优化器求解的极端情况。处理约束本文例子中上层只有边界约束。如果上层也有复杂约束需要在PSO评估粒子后增加一个约束处理机制。常用方法包括罚函数法将违反约束的程度加到目标函数上使其变差或修复法将不可行解投影到可行域边界上。6.2 参数调优实战心得PSO的性能对参数敏感。以下是一些调优经验收敛过快或陷入局部最优如果收敛曲线很早就变平但结果不理想可能是陷入了局部最优。尝试增加SwarmSize更多粒子增大w_max如0.95以增强探索能力或略微增大c1如2.0以增强个体多样性。振荡不收敛如果曲线上下跳动始终无法稳定。尝试减小w_min如0.2减小Vmax或增大c2如2.8以增强向群体最优学习的导向性。早熟收敛算法初期就收敛到一个点可能是Vmax太小或w衰减太快。尝试增大Vmax或者采用非线性递减的w如w w_max * (w_min/w_max)^(iter/MaxIter)。通用策略参数自适应是高级技巧。例如可以根据粒子群的聚集程度如粒子间距离的方差动态调整w、c1、c2当群体过于分散时增强探索过于集中时增强开发。6.3 常见问题与解决方案速查表问题现象可能原因排查步骤与解决方案下层求解频繁失败(exitflag 0)上层粒子x的取值使得下层问题可行域为空。1. 检查solve_lower_problem函数中的约束转换是否正确。2. 打印出导致失败的x值手动验证下层约束是否矛盾。3. 收紧上层变量x的搜索边界 (x_lb,x_ub)确保在边界内采样时下层问题有较高概率可行。PSO结果每次都不一样且波动大算法随机性大可能未收敛。1. 增加MaxIter观察收敛曲线是否在后期平稳。2. 增大SwarmSize使用更多的粒子进行搜索。3. 多次运行如30次取统计意义上的最佳结果或平均结果作为最终输出。算法运行速度极慢下层问题求解复杂或粒子群/迭代次数设置过大。1. 优化solve_lower_problem函数确保求解器选项设置正确如关闭显示。2. 对于线性下层问题linprog速度很快。如果慢检查是否是上层维度或粒子数过高。3. 考虑使用并行计算MATLAB的parfor可以并行评估粒子群大幅加速。找到的解明显不优算法陷入局部最优或参数设置不当。1. 使用参数调优策略见6.2节。2. 尝试不同的智能算法如遗传算法、差分进化替换PSO模块对比效果。3. 如果问题规模不大可以尝试在PSO找到的解附近用局部搜索算法如fmincon进行精细优化。“索引超出矩阵维度”等错误代码维度不匹配常见于扩展为多维时。1. 仔细检查所有矩阵和向量的初始化维度确保dim定义正确。2. 在更新velocity和particles_x时确保是对所有维度进行操作。使用size()函数调试。这个基于智能优化算法的双层问题求解框架其价值在于提供了一种清晰、模块化且易于理解的求解范式。它可能不是求解速度最快的但对于理解双层优化的本质、快速验证问题模型、以及为更复杂的实际应用搭建原型是一个非常有力的工具。当你需要处理一个全新的、结构复杂的双层优化问题时不妨先用这个框架跑通得到一个基准解然后再去探索更高效、更专用的算法。