烟花算法(FWA)实战:从原理到MATLAB实现与优化策略解析
1. 烟花算法FWA初印象从夜空灵感说起想象一下你在一个晴朗的夏夜仰望星空突然一束烟花升空在最高点“砰”地一声炸开无数绚烂的火花向四面八方散开照亮了周围一片区域。这些火花有的亮有的暗有的飞得远有的落得近。烟花算法Fireworks Algorithm, FWA的灵感就来源于这个美丽的自然现象。我第一次接触这个算法时就觉得这个比喻太妙了它把抽象的优化搜索过程变成了一个我们都能理解的生动画面。简单来说烟花算法是一种群智能优化算法。你可能听说过粒子群PSO或者蚁群ACO它们都属于这个大家族。这类算法的核心思想是模拟自然界中生物群体的集体智慧通过个体间的简单交互和信息共享来解决复杂的数学优化问题。而FWA就是由北京大学的谭营教授在2010年提出的它用“烟花”和“火花”来比喻搜索过程中的候选解。那么它能做什么呢假设你是一个工程师需要为新产品设计一个参数组合使得性能最好、成本最低或者你是一个研究员需要在一个复杂的函数中找到那个“最低点”全局最优解。这些问题往往像在一片漆黑的山脉里寻找最低的谷底传统方法容易掉进某个小坑局部最优就出不来了。FWA就像同时发射多枚烟花让它们在夜空中爆炸火花四处探索。适应度好性能优的烟花就像特别亮的那个它会在自己周围一个较小的范围内产生大量密集的火花进行精细的局部搜索而适应度差的烟花则会在一个更大的范围内产生较少的火花进行广泛的全局探索。这种机制巧妙地平衡了“深入挖掘”和“四处勘探”两种搜索策略。我刚开始用MATLAB实现它的时候最直观的感受就是它的结构清晰。整个算法流程可以拆解成三个核心步骤就像放烟花的三个动作资源分配决定每个烟花炸多响、出多少火花、爆炸行为火花具体往哪里飞和选择策略哪些火花能成为下一代的烟花。这个框架非常容易理解和编码特别适合我们这些喜欢从原理入手再动手实践的工程师。接下来我就带你一步步拆解这个有趣的过程并用MATLAB把它实现出来。2. 核心原理拆解烟花是怎么“炸”出最优解的理解了烟花算法的比喻我们得深入它的“火药配方”和“爆炸力学”。别看原理听着像看烟花表演背后的数学设计可是非常精巧的直接决定了算法能不能又快又准地找到目标。我当初啃论文时觉得有几个公式是关键弄懂了它们整个算法就通了。2.1 资源分配好烟花多炸差烟花远炸这是FWA最核心的智慧。算法一开始会在解空间里随机放置N个烟花初始解。每个烟花的位置代表一组可能的解。接下来不是所有烟花都平等对待算法要根据它们的“质量”即适应度值目标函数值越小通常代表越好来分配“火药”。火花数量计算对于一个烟花xi它产生的火花数量Si由以下公式决定Si m * ( (f_max - f(xi)) ε ) / ( Σ (f_max - f(xj)) ε )这里m是一个控制火花总数的常数f_max是当前所有烟花中最差的适应度值f(xi)是当前烟花的适应度ε是一个极小的数比如1e-8防止分母为零。这个公式什么意思呢适应度越好值越小的烟花(f_max - f(xi))这个差值就越大因此分到的火花数Si就越多。反之差的烟花火花就少。这确保了优秀的个体有更多资源在其附近进行精细搜索。爆炸幅度计算光有数量不够还得决定炸开的范围。爆炸幅度Ai的计算公式是Ai A * ( f(xi) - f_min ε ) / ( Σ (f(xj) - f_min) ε )这里A是一个常数代表最大爆炸幅度f_min是当前最好的适应度值。注意看适应度越差的烟花(f(xi) - f_min)越大计算出的爆炸幅度Ai就越大。这意味着差的烟花会在一个更大的范围内“碰运气”探索未知区域而好烟花则在很小的范围内“精耕细作”。我在MATLAB里实现这两个函数时通常会加上边界限制防止火花数过多或过少爆炸幅度也不会无限大。比如火花数Si会被限制在[S_min, S_max]之间。这种自适应分配机制是FWA平衡勘探Exploration探索全局和开发Exploitation挖掘局部的关键。2.2 爆炸与变异火花的飞行轨迹资源分配好了烟花就该“炸”了。每个烟花会根据计算出的火花数Si和幅度Ai在其周围产生火花。这里有两种主要的火花类型1. 爆炸火花这是主力军。对于烟花的每一个维度决策变量算法会随机选择一个位移方向位移的大小是在[-Ai, Ai]区间内均匀随机取的一个值。用公式表示就是x_new(k) x_old(k) U(-Ai, Ai)其中U表示均匀分布。这个过程会在多个随机选择的维度上重复从而产生一个新的火花位置。这模拟了爆炸后火花向各个方向飞溅的效果。2. 高斯变异火花为了增加种群的多样性避免所有火花都挤在烟花附近算法还会产生少量经过高斯变异Gaussian Mutation的火花。具体做法是随机选择一个烟花对其每个维度乘以一个服从高斯分布N(1,1)的随机数x_new x_old * Gaussian(1, 1)高斯变异就像给火花加了一个随机扰动有可能让它跳到离原烟花较远的地方从而帮助算法跳出可能陷入的局部最优区域。在实际编码中我通常只对少数几个火花比如5个进行高斯变异这个比例不能太高否则会破坏算法的收敛性。映射规则爆炸或变异后火花的位置可能会超出问题的定义域比如参数取值范围。这时就需要一个映射规则把它“拉”回来。最常用的是模运算映射if x_new(k) LB or x_new(k) UB: x_new(k) LB mod( |x_new(k) - LB|, (UB - LB) )LB和UB分别是该维度的下界和上界。mod是取模运算这相当于让火花在边界上“反弹”回可行域内。2.3 选择策略优胜劣汰生生不息一轮爆炸结束后天空中会有很多火花包括爆炸火花、高斯火花以及原来的烟花。下一代该由哪些“个体”作为烟花继续爆炸呢这里采用了精英保留与距离多样性相结合的策略。首先当前适应度最好的个体可以是烟花或火花会被无条件保留到下一代。这是精英策略保证了算法不会遗忘已找到的最优解。然后为了保持种群的多样性避免所有烟花都聚集到一点剩下的N-1个位置需要通过概率选择。这个概率与个体之间的距离有关。计算每个个体除了已选出的最优个体到所有其他个体的欧式距离之和R(xi)。个体xi被选中的概率p(xi)为p(xi) R(xi) / Σ R(xj)距离其他个体越远的火花被选中的概率越大。这个设计非常巧妙它鼓励选择那些分布在稀疏区域的个体从而迫使烟花算法去探索解空间中尚未被充分搜索的区域。我常用轮盘赌的方法来实现这个概率选择。通过这三步——分配、爆炸、选择——的不断迭代烟花种群就像一次次燃放的烟花秀逐渐向问题的最优解区域汇聚。整个原理听起来复杂但用MATLAB实现起来你会发现它的模块化非常好每个步骤都可以封装成一个清晰的函数。3. 手把手MATLAB实现从零搭建你的第一个FWA理论懂了不敲代码都是纸上谈兵。下面我就带你用MATLAB从零开始搭建一个标准的烟花算法。我会把关键代码拆开揉碎了讲你完全可以跟着一步步做出来。我用的版本是MATLAB R2021a但大部分代码兼容性很好。3.1 环境准备与参数设置首先我们创建一个主脚本文件比如叫main_FWA.m。第一步是定义算法的各种参数这部分就像给烟花表演定下规则有多少发烟花每发最多炸出多少火花等等。%% 主函数烟花算法(FWA)测试 clear all; close all; clc; % 算法参数设置 params.dim 30; % 问题的维度比如你要优化30个参数 params.seednum 5; % 初始烟花的数量 (N) params.sonnum 50; % 火花总数常数 (m) params.maxEva 20000; % 最大函数评估次数 (终止条件) params.modStep 100; % 记录最优值的间隔 params.maxEva_mod100 params.maxEva / params.modStep; params.gaussianNum 5; % 高斯变异火花的数量 % 爆炸火花数限制参数 global Max_Sparks_Num Min_Sparks_Num Coef_Spark_Num; Max_Sparks_Num 40; % 单个烟花最大火花数 (a) Min_Sparks_Num 2; % 单个烟花最小火花数 (b) Coef_Spark_Num 50; % 火花总数常数 (m)同上这里用全局变量 % 爆炸幅度常数 global Coef_Explosion_Amplitude; Coef_Explosion_Amplitude 40; % 最大爆炸幅度常数 (A) % 测试函数设置 - 这里以经典的Sphere函数为例 params.fun_name fun_sphere; params.lowerBound -100; % 搜索空间下界 params.upperBound 100; % 搜索空间上界 params.lowerInit 50; % 烟花初始位置下界 params.upperInit 100; % 烟花初始位置上界 params.optimum 0; % 理论最优值 (Sphere函数在0点取得最小值0)这里我定义了一些全局变量比如Max_Sparks_Num是为了在后续的子函数中方便调用。fun_sphere是我们待会儿要自己写的目标函数。3.2 核心函数实现接下来我们实现算法最核心的几个函数。我们先写目标函数fun_sphere.m它计算一个解向量的Sphere函数值各维度平方和。这是一个单峰函数常用于测试算法的收敛精度。% 文件fun_sphere.m function fitness fun_sphere(x) % Sphere函数最优值在原点f(0,...,0)0 fitness sum(x.^2); end然后是计算每个烟花火花数量的函数sonsnum_cal.m。这里完全按照2.1节的公式实现并加入了上下限裁剪。% 文件sonsnum_cal.m function sonsnum_array sonsnum_cal(fitness_array, params) % 输入fitness_array - 所有烟花的适应度值数组 % params - 参数结构体 % 输出sonsnum_array - 每个烟花应产生的火花数数组 global Max_Sparks_Num Min_Sparks_Num Coef_Spark_Num; fitness_max max(fitness_array); % 最差适应度值 f_max fitness_sub_max abs(fitness_max - fitness_array); % 计算差值 fitness_sub_max_sum sum(fitness_sub_max); % 差值总和 sonsnum_array zeros(1, params.seednum); % 初始化火花数数组 eps 1e-8; % 防止除零的小量 for i 1:params.seednum % 计算比例 sonnum_temp (fitness_sub_max(i) eps) / (fitness_sub_max_sum eps); % 乘以总火花常数并四舍五入 sonnum_temp round(sonnum_temp * Coef_Spark_Num); % 上下限裁剪 if sonnum_temp Max_Sparks_Num sonnum_temp Max_Sparks_Num; elseif sonnum_temp Min_Sparks_Num sonnum_temp Min_Sparks_Num; end sonsnum_array(i) sonnum_temp; end end接着是计算爆炸幅度的函数scope_cal.m。% 文件scope_cal.m function scope_array scope_cal(fitness_array, params) % 输入fitness_array - 所有烟花的适应度值数组 % params - 参数结构体 % 输出scope_array - 每个烟花的爆炸幅度数组 global Coef_Explosion_Amplitude; fitness_best min(fitness_array); % 最佳适应度值 f_min fitness_sub_best abs(fitness_best - fitness_array); % 计算差值 fitness_sub_best_sum sum(fitness_sub_best); % 差值总和 scope_array zeros(1, params.seednum); eps 1e-8; for i 1:params.seednum scope_array(i) Coef_Explosion_Amplitude * (fitness_sub_best(i) eps) / (fitness_sub_best_sum eps); end end现在是最复杂的部分生成爆炸火花和高斯变异火花。我们写一个函数sons_generate.m来生成爆炸火花并处理越界映射。% 文件sons_generate.m function [sons_matrix, sons_fitness_array, fitness_best_array] ... sons_generate(sonsnum_array, scope_array, seeds_matrix, params, fitness_best_array) global total_fiteval_times evaTime; % 全局计数器 % 计算本轮要生成的总火花数 fiteval_time sum(sonsnum_array); total_fiteval_times total_fiteval_times fiteval_time; % 初始化火花位置矩阵和适应度数组 sons_matrix zeros(fiteval_time, params.dim); sons_fitness_array zeros(1, fiteval_time); sons_index 1; % 火花矩阵的索引 % 遍历每个烟花生成其爆炸火花 for i 1:params.seednum for j 1:sonsnum_array(i) seed_position seeds_matrix(i, :); % 获取当前烟花位置 allowed_dim_array ones(1, params.dim); % 标记哪些维度已被扰动 % 随机决定要扰动多少个维度至少1个 dimens_select ceil(rand * params.dim); % 计算本次爆炸的位移量在[-Ai, Ai]内均匀随机 offset (rand * 2 - 1) * scope_array(i); % 随机选择dimens_select个维度进行扰动 for k 1:dimens_select rand_dimen ceil(rand * params.dim); % 确保不重复扰动同一维度 while allowed_dim_array(rand_dimen) 0 rand_dimen ceil(rand * params.dim); end allowed_dim_array(rand_dimen) 0; % 执行位移操作 seed_position(rand_dimen) seed_position(rand_dimen) offset; % 越界检查与映射 if seed_position(rand_dimen) params.upperBound || seed_position(rand_dimen) params.lowerBound span params.upperBound - params.lowerBound; seed_position(rand_dimen) params.lowerBound mod(abs(seed_position(rand_dimen) - params.lowerBound), span); end end % 存储生成的火花 sons_matrix(sons_index, :) seed_position; sons_index sons_index 1; end end % 计算所有新生成火花的适应度并更新历史最优 for i 1:fiteval_time sons_fitness_array(i) feval(params.fun_name, sons_matrix(i, :)); % 更新全局最优适应度记录 if sons_fitness_array(i) fitness_best_array(evaTime) fitness_best_array(evaTime 1) sons_fitness_array(i); else fitness_best_array(evaTime 1) fitness_best_array(evaTime); end evaTime evaTime 1; % 函数评估次数1 end end高斯变异函数seedGaussMutation.m相对简单。% 文件seedGaussMutation.m function [SeedsMatrixGauss, SeedsFitGaussMutation, fitness_best_array] ... seedGaussMutation(SeedsMatrix, params, fitness_best_array) global evaTime; gaussianNum params.gaussianNum; dim params.dim; SeedsMatrixGauss zeros(gaussianNum, dim); SeedsFitGaussMutation zeros(1, gaussianNum); for k 1:gaussianNum % 随机选择一个烟花进行变异 idx ceil(rand * size(SeedsMatrix, 1)); gauss_spark SeedsMatrix(idx, :); % 对每个维度施加高斯扰动 N(1,1) for d 1:dim gauss_spark(d) gauss_spark(d) * normrnd(1, 1); % 越界映射 if gauss_spark(d) params.upperBound || gauss_spark(d) params.lowerBound span params.upperBound - params.lowerBound; gauss_spark(d) params.lowerBound mod(abs(gauss_spark(d) - params.lowerBound), span); end end SeedsMatrixGauss(k, :) gauss_spark; SeedsFitGaussMutation(k) feval(params.fun_name, gauss_spark); % 更新历史最优 if SeedsFitGaussMutation(k) fitness_best_array(evaTime) fitness_best_array(evaTime 1) SeedsFitGaussMutation(k); else fitness_best_array(evaTime 1) fitness_best_array(evaTime); end evaTime evaTime 1; end end最后是实现基于距离的概率选择策略的函数selectNextIterationOnEntropy.m。这里我用了“熵”这个词但核心就是基于距离的轮盘赌选择。% 文件selectNextIterationOnEntropy.m function [SeedsMatrix, SeedsFitness] selectNextIterationOnEntropy(AllMatrix, AllFitness, params) pop_size size(AllMatrix, 1); % 当前所有个体烟花火花的数量 nextSeedsNum params.seednum; % 下一代烟花数量 % 第一步精英选择保留适应度最好的个体 [~, bestIdx] min(AllFitness); SeedsMatrix AllMatrix(bestIdx, :); SeedsFitness AllFitness(bestIdx); % 从候选池中移除已选出的最优个体 candidateMatrix AllMatrix; candidateFitness AllFitness; candidateMatrix(bestIdx, :) []; candidateFitness(bestIdx) []; % 计算剩余每个个体到所有其他个体的距离之和 numCandidates size(candidateMatrix, 1); distance_sum zeros(1, numCandidates); for i 1:numCandidates % 计算个体i到所有其他个体包括已选出的精英的欧式距离 diff bsxfun(minus, [SeedsMatrix; candidateMatrix], candidateMatrix(i, :)); dist sqrt(sum(diff.^2, 2)); distance_sum(i) sum(dist); end % 根据距离之和计算被选中的概率距离越大概率越高 selection_prob distance_sum / sum(distance_sum); % 使用轮盘赌选择剩下的 nextSeedsNum-1 个个体 for n 2:nextSeedsNum if isempty(candidateMatrix) break; end r rand; cum_prob cumsum(selection_prob); selectedIdx find(cum_prob r, 1); % 将选中的个体加入下一代烟花 SeedsMatrix [SeedsMatrix; candidateMatrix(selectedIdx, :)]; SeedsFitness [SeedsFitness, candidateFitness(selectedIdx)]; % 从候选池中移除已选中的个体 candidateMatrix(selectedIdx, :) []; candidateFitness(selectedIdx) []; distance_sum(selectedIdx) []; % 重新计算概率 if ~isempty(distance_sum) selection_prob distance_sum / sum(distance_sum); end end end3.3 算法主循环与结果可视化把上面的函数都准备好后我们就可以编写烟花算法的主函数opt_FWA.m了。它负责串联整个迭代流程。% 文件opt_FWA.m function [fitness_best_array_return, time_return] opt_FWA(params) tic; % 开始计时 % 声明全局变量 global total_fiteval_times Coef_Explosion_Amplitude Max_Sparks_Num Min_Sparks_Num Coef_Spark_Num evaTime; % 初始化全局变量 total_fiteval_times 0; Coef_Explosion_Amplitude 40; Max_Sparks_Num 40; Min_Sparks_Num 2; Coef_Spark_Num 50; evaTime 0; % 1. 初始化随机生成初始烟花种群 SeedsMatrix zeros(params.seednum, params.dim); for i 1:params.seednum SeedsMatrix(i, :) params.lowerInit rand(1, params.dim) .* (params.upperInit - params.lowerInit); end % 计算初始种群的适应度 SeedsFitness zeros(1, params.seednum); fitness_best_array zeros(1, params.maxEva); % 记录每次评估的最优值 for i 1:params.seednum SeedsFitness(i) feval(params.fun_name, SeedsMatrix(i, :)); evaTime evaTime 1; if i 1 fitness_best_array(evaTime) SeedsFitness(i); else fitness_best_array(evaTime) min(SeedsFitness(i), fitness_best_array(evaTime-1)); end end % 2. 主迭代循环 while evaTime params.maxEva % a. 计算每个烟花的火花数和爆炸幅度 sonsnum_array sonsnum_cal(SeedsFitness, params); scope_array scope_cal(SeedsFitness, params); % b. 生成爆炸火花 [SonsMatrix, SonsFitness, fitness_best_array] sons_generate(sonsnum_array, scope_array, SeedsMatrix, params, fitness_best_array); % c. 生成高斯变异火花 [SeedsMatrixGauss, SeedsFitGaussMutation, fitness_best_array] seedGaussMutation(SeedsMatrix, params, fitness_best_array); % d. 合并所有个体当前烟花爆炸火花高斯火花 AllMatrix [SeedsMatrix; SonsMatrix; SeedsMatrixGauss]; AllFitness [SeedsFitness, SonsFitness, SeedsFitGaussMutation]; % e. 选择下一代烟花 [SeedsMatrix, SeedsFitness] selectNextIterationOnEntropy(AllMatrix, AllFitness, params); end time_return toc; % 结束计时 fprintf(\nFWA运行结束。找到的最优值: %.10f, 耗时: %.4f 秒\n, fitness_best_array(params.maxEva), time_return); % 返回按间隔记录的最优值用于画图 fitness_best_array_return zeros(1, params.maxEva_mod100); for i 1:params.maxEva_mod100 fitness_best_array_return(i) fitness_best_array(i * params.modStep); end end最后在主脚本main_FWA.m中调用主函数并绘制收敛曲线。%% 接 main_FWA.m 参数设置部分之后 % 运行算法 repetitions 1; % 运行次数可改为多次取平均 fit_fwa_matrix zeros(repetitions, params.maxEva_mod100); time_fwa_array zeros(1, repetitions); for time_index 1:repetitions [fit_fwa_matrix(time_index, :), time_fwa_array(time_index)] opt_FWA(params); end % 绘制收敛曲线 figure; plot((1:params.maxEva_mod100)*params.modStep, fit_fwa_matrix(1, :), b-, LineWidth, 1.5); xlabel(函数评估次数 (FEs)); ylabel(最佳适应度值 (fitness)); title(烟花算法 (FWA) 在Sphere函数上的收敛曲线); grid on; set(gca, YScale, log); % 对数坐标更易观察收敛运行这段代码你就能看到FWA在30维Sphere函数上的搜索过程。曲线会随着评估次数的增加而下降最终逼近0。这就是你亲手实现的第一个烟花算法自己跑一遍再对照原理看看每个变量的变化理解会深刻得多。4. 进阶优化策略EFWA与dynFWA深度解析标准FWA虽然巧妙但在实际应用中尤其是面对高维、复杂的优化问题时还是会暴露出一些局限性。比如爆炸幅度衰减过快导致后期搜索乏力或者高斯变异效率不高等。学术界提出了很多改进版本其中增强烟花算法EFWA和动态搜索烟花算法dynFWA是两个非常重要且实用的变体。我在项目里都尝试过效果提升确实明显。4.1 增强烟花算法EFWA修补标准FWA的五大短板EFWA可以看作是对标准FWA的一次全面“打补丁”升级。它主要针对原始算法的五个问题进行了改进1. 最小爆炸幅度检查在标准FWA中优秀烟花的爆炸幅度Ai会随着迭代变得非常小甚至接近于零。这导致后期局部搜索能力几乎丧失。EFWA引入了一个最小爆炸幅度下限A_min。计算出的Ai如果小于A_min则将其设置为A_min。A_min通常随着迭代次数增加而动态减小例如A_min A_init * ( (maxIter - curIter) / maxIter )^α其中α是一个常数。这个策略保证了算法在后期仍有一定的探索能力。2. 新的映射规则标准FWA使用模运算进行越界映射这可能导致解被映射到边界附近造成种群多样性损失。EFWA采用了随机映射如果一个维度x越界则将其重新随机初始化到搜索空间内x_new LB rand * (UB - LB)。这种方法简单能更好地保持多样性。3. 新的高斯变异算子标准FWA的高斯变异是对整个向量乘以一个高斯随机数。EFWA改为只随机选择其中一个维度进行高斯变异x_new(k) x_old(k) * Gaussian(1, 1)其他维度保持不变。这种维度变异的策略更精细扰动更有针对性效率更高。4. 精英策略的引入在EFWA的选择操作中不仅保留全局最优个体还会保留一定数量的当前最优火花作为下一代烟花。这加强了对优质区域的开发能力。5. 改进的选择策略EFWA通常采用更直接的选择方式比如直接选择适应度最好的前N个个体作为下一代烟花或者结合一定概率的随机选择计算上比基于距离的选择更高效。实现EFWA时主要就是修改scope_cal函数加入最小幅度限制修改映射规则和高斯变异函数。改动点清晰效果提升却很大特别是在处理多峰函数时能更有效地跳出局部最优。4.2 动态搜索烟花算法dynFWA核心烟花引领搜索dynFWA的思维更进一步它不再对所有烟花“一视同仁”而是进行了角色分工。它定义了一个“核心烟花Core Firework, CF”即当前迭代中适应度最好的那个烟花。CF的任务是进行局部精细搜索开发而其他普通烟花N-1个则负责全局探索勘探。核心思想对于核心烟花CF它的爆炸幅度A_CF是动态自适应调整的。如果CF在上一轮迭代中找到了更优的解即成功改进说明当前搜索区域有希望那么下一轮就缩小A_CF进行更精细的挖掘。如果没有改进则增大A_CF扩大搜索范围。调整规则通常是一个乘法因子A_CF(t1) A_CF(t) * η其中η 1表示增大η 1表示缩小。对于普通烟花它们的爆炸幅度计算方式与EFWA类似但通常会设置一个比CF更大的初始幅度以确保全局探索能力。优势这种分工明确、动态调整的策略使得算法能更智能地平衡勘探与开发。CF专注于在最有希望的区域“挖矿”而其他烟花则在广阔空间“探矿”。实测下来dynFWA在收敛速度和精度上通常比EFWA更有优势尤其是对于单峰或存在明显全局最优盆地的问题。代码实现关键你需要维护两个爆炸幅度常数一个给CF用动态变化一个给普通烟花用可以静态或按EFWA规则变化。在选择出下一代烟花后立即识别出CF并根据其本次迭代是否改进来更新A_CF。4.3 策略对比与应用场景选择为了更直观我把这几种策略的特点和适用场景总结了一下特性标准FWAEFWAdynFWA核心改进基础框架修复五大缺陷核心烟花动态调幅角色分工爆炸幅度自适应计算无下限自适应计算有最小下限CF动态调整普通烟花自适应映射规则模运算映射随机映射通常沿用EFWA的随机映射高斯变异全维度变异单维度变异通常沿用或简化/去除选择策略基于距离的概率选择精英保留随机/择优选择精英保留明确CF角色计算复杂度中等中等略低选择策略更简单优点原理清晰易于实现鲁棒性更强避免早熟收敛快精度高自适应能力强缺点后期易停滞映射可能损多样性参数稍多需设A_min对CF的依赖较强若CF陷入局部最优可能影响全局适用场景教学、理解原理、简单问题大多数实际优化问题特别是多峰、复杂问题单峰或存在明显全局最优的问题追求快速收敛怎么选呢我的经验是如果你是初学者想理解原理从标准FWA实现开始绝对没错。它的代码清晰地反映了核心思想。当你需要解决一个实际的工程优化问题比如参数调优、神经网络结构搜索时EFWA通常是更稳妥、更通用的选择它修复了标准版的明显短板。而如果你的问题目标函数计算非常耗时你希望尽快得到一个不错的解或者问题本身是单峰的那么dynFWA会是更好的选择它的收敛速度往往更快。在实际项目中我常常会先跑一下EFWA看看收敛情况和解的质量如果发现收敛速度不够快再尝试切换到dynFWA。有时候甚至可以根据迭代进程混合使用策略前期用dynFWA快速下降后期切换到EFWA进行精细搜索这也是一个有趣的优化方向。5. 实战技巧与避坑指南纸上得来终觉浅绝知此事要躬行。自己动手实现和调参的过程中肯定会遇到各种问题。我结合自己踩过的坑分享几个实战技巧希望能帮你少走弯路。1. 参数调优不是玄学有迹可循烟花算法的参数不算多但每个都影响性能。N烟花数和m火花总数是基础。N太小种群多样性不足N太大计算开销剧增。对于大多数问题N设置在5到10之间是个不错的起点。m通常设为N的5到10倍比如N5, m50。A最大爆炸幅度和A_minEFWA中的最小幅度是关键。A太大搜索过于随机太小则跳不出局部。一个经验法则是将其设置为搜索空间范围UB-LB的10%到20%。A_min的衰减系数α通常取1到3之间需要根据问题复杂度微调。 我的建议是先用默认参数在简单测试函数如Sphere上跑通记录收敛曲线。然后每次只调整一个参数观察曲线变化。你会直观地看到A影响搜索范围N和m影响搜索密度。2. 处理高维问题的策略当问题维度很高比如1000维以上时标准FWA可能会遇到“维数灾难”。爆炸火花在超高维空间中随机扰动几个维度就像大海捞针效率很低。这里有几个对策增加爆炸火花的维度扰动数在sons_generate函数中不要只随机扰动少数几个维度可以按一定比例如维度的10%随机选择维度进行扰动。采用分组爆炸策略将高维变量分成若干组每次爆炸只针对某一组变量进行轮流进行。这有点像坐标下降法能更有效地在子空间内搜索。结合局部搜索算子在FWA迭代后期引入像拟牛顿法、共轭梯度法等局部搜索方法对最优解附近进行精细打磨可以显著提高解的质量。3. 跳出局部最优的“组合拳”算法陷入局部最优时别急着换算法可以先试试这些“组合拳”调整高斯变异增加高斯变异火花的数量或者增大高斯变异的方差比如从N(1,1)改为N(1,2)给予更强的扰动。周期性重置每隔一定代数保留最优的1-2个个体其余个体在搜索空间内重新随机初始化。这相当于给算法一次“重启”机会。混合其他算法的变异策略比如引入差分进化DE的变异策略来生成部分火花或者引入模拟退火的概率接受劣解机制。我在一个多峰优化项目里将FWA的爆炸火花与DE的“DE/rand/1”变异结合效果很好。4. 代码调试与性能分析写MATLAB代码时善用tic/toc计时用profile工具查看函数耗时。你会发现距离计算在选择策略中往往是计算瓶颈尤其是种群规模大、维度高的时候。可以尝试使用向量化计算代替循环来计算欧式距离。在非严格必要的情况下简化选择策略。EFWA和dynFWA采用的精英选择随机选择就比标准FWA的基于距离的选择快得多。设置一个合理的最大函数评估次数maxEva作为终止条件比单纯设置最大迭代次数更公平也更能反映算法效率。最后也是最重要的一点可视化是你的好朋友。对于二维问题一定要把烟花和火花的分布图画出来动态展示迭代过程。你会亲眼看到烟花如何聚集、扩散对算法行为有最直观的理解。即使对于高维问题也可以绘制最优适应度随评估次数的下降曲线对比不同参数和策略的效果。调试算法时我一半的时间都在看各种图这比干看数字有效得多。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2417536.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!