海豚回波定位算法(DEA)
内容
引言
随着每一篇新文章的发表,我们都离目标更近了一步:为我们的交易机器人找到一种合适的算法来解决优化问题。今天我们将探索另一种受自然界启发的算法,它基于某些海洋动物的回声定位能力。
想象一只海豚在漆黑的深海中捕猎。能见度几乎为零,但这并不妨碍它成功地找到猎物。秘密在于一种惊人的能力:海豚发出一系列脉冲声,然后利用反射回来的回声,构建出周围空间的精确图像。一个有趣的事实是,这些脉冲声的频率是变化的:在大范围搜索时脉冲声稀疏,而当目标靠近时则变得密集。正是这种不同寻常的策略构成了 DEA(海豚回声定位算法)的基础。
在优化领域,我们经常面临类似的挑战:在广阔的可能性空间中寻找最优解,却无法掌握全局信息。就像海洋中的海豚一样,我们从广泛搜索开始,然后逐渐将注意力集中在最有前景的区域。
算法的实现
为了更好地理解算法的工作原理,让我们想象这样一个场景。你和朋友们在一片广阔的海滩上寻找黄金,每人都拿着金属探测器。搜索开始时,分散到整个区域是合理的 —— 这样你们更有可能偶然发现有趣的东西。但一旦其中一人听到强烈的信号,他就会通知其他人,渐渐地整个团队开始集中到有前景的地方。到搜索结束时,所有人都在最强信号附近挖掘。这就是海豚回声定位算法的精髓。
在算法中,海豚的角色由搜索代理扮演 —— 即解空间中的点。每只这样的 "海豚" 都代表问题的一个潜在解。例如,如果我们在寻找简单函数 y = x² 的最小值,那么一只海豚可能在点 x = -3 处(此处 y = 9),另一只在点 x = 1 处(此处 y = 1),第三只可能随机落在点 x = 0 处(此处 y = 0)—— 当前表现最好的海豚。
但海豚是如何交换信息的呢?这就引出了有效半径的概念,记为 "Re"。想想手电筒的光能传播多远。当 Re = 1 时,我们得到一束狭窄的光,只能照亮紧邻的区域。当 Re = 3 时,光线散布得更宽,覆盖更多空间。当 Re = 5 或更高时,搜索行为更像是泛光灯。在算法的语境中,这意味着关于优质解的信息会传播到邻近区域,且这种影响的强度随距离递减。
所有这些信息都以 "前景图" 的形式累积起来,算法称之为累积适应度(AF)。想象一下城市的热力图,其中 "热" 区表示高活动区域。在我们的例子中,"热" 区是海豚找到优质解(猎物)的区域。某个区域的成功发现越多,它就越 "热",从而吸引更多海豚。
然而,算法比简单地聚集在一个地方要聪明得多。它使用一个称为预定概率(PP)的参数来控制探索新区域与开发已发现优质地点之间的平衡。初始阶段,当 PP 较低(约 0.1)时,算法进行探索实验;随后,当 PP 约为 0.5 时,它更多地依赖经过验证的方法;而当 PP 接近 1 时,它只使用最有效的方法。
让我们来看一个算法工作原理的具体例子。假设我们正在寻找一片丘陵地带的最高点。搜索开始时,我们的海豚随机散布在整个区域 —— 有些在山谷,有些在山坡,有些则幸运地落在山顶。在评估了每个位置的高度后,算法确定位于山顶的海豚找到了最佳地点。现在有趣的事情发生了:这只幸运海豚周围的区域对其他海豚变得更有吸引力,但 —— 这是关键点 —— 领导者所在的那个点会被暂时 "清零"。这迫使其他海豚探索附近区域,而不是聚集在一个点上。这种方法有助于避免过早收敛,并找到其他潜在的优质解。
随着算法的运行,情况会发生变化。在第 50 次迭代时,我们看到海豚开始聚集在丘陵区域,但仍保持一定的多样性。到第 100 次迭代时,它们中的大多数都集中在地形的最高点,而到最后,所有海豚都聚集在全局最优点。算法的效率取决于参数的正确配置。
有效半径 Re 的选择对于平衡局部搜索和全局搜索至关重要。当 Re = 1 时,我们得到非常精确但范围狭窄的搜索 —— 就像用放大镜在干草堆里找针一样。将 Re 增加到 3-4 可以得到平衡的方法,类似于用手电筒搜索。而当 Re 大于 5 时,算法像探照灯一样工作,覆盖更大的区域,但会损失精度。
Power 参数决定了算法从探索切换到开发的剧烈程度。当 Power = 1 时,这种过渡是平滑且渐进的。Power 值为 2(推荐值)会产生更明显的过渡,而 Power 值为 3 及以上时会产生更急剧的过渡,这对某些类型的任务可能很有用。
初始概率 PP1 设定了探索与利用之间的起始平衡。低值(0.05)意味着对空间进行激进探索,标准值 0.1 提供了良好的平衡,而高值(0.5)则导致快速聚焦于已找到的解。
DEA 的主要优势在于其适应性 —— 算法自动调整探索新区域与深入有前景区域之间的平衡。同时它保持相对简单,只需要配置四个参数。通过累积适应度传播信息的机制可以高效利用关于搜索空间的知识,而对最佳位置的 AF 清零则有助于避免陷入局部最优。
当然,该算法也有一些局限性。它需要额外的内存来存储所有备选方案的累积适应度,这对于非常大的搜索空间可能是个问题。还有一点:算法的效率取决于有效半径 Re 的正确选择。以下是算法运行原理的示意图。

图 1. DEA 算法运行示意
图中展示了 DEA 算法的主要阶段:
- 初始探索—— 海豚(搜索代理)处于随机位置,具有 Re 回声定位半径。
- 累积适应度(AF)分布——"AF" 如何在海豚位置周围累积。
- 收敛过程由三个子阶段组成,展示了从探索到开发的过渡。
该图示帮助我们理解海豚如何利用回声定位找到最优解、信息如何通过累积适应度传播、参数 "Re" 如何影响搜索宽度,以及 "PP" 如何控制探索与利用之间的平衡。核心概念——PP(预定概率)和 AF 的公式。算法流程图—— 带循环的基本步骤。Re 参数的影响—— 窄、中、宽三种影响半径的可视化。
图中的配色方案:蓝色 —— 普通海豚,红色 —— 最优解,蓝色渐变 —— 累积适应度水平,灰色 —— 搜索空间。
现在我们对算法有了一定了解,可以开始编写详细的伪代码了。
输入:
- 海豚数量(搜索代理)
- 有效回声定位半径
- 收敛曲线幂次
- 初始预定概率
- 搜索空间边界和每个维度的采样步长
初始化:
创建备选方案空间:
- 对搜索空间的每个维度,生成一组可能的备选方案
- 如果指定了采样步长,则按该步长从最小值到最大值创建备选方案
- 如果未指定步长,则生成 500 个均匀分布的备选方案
- 检查有效半径:不应超过备选方案数量的四分之一
海豚初始放置:
- 将所有海豚随机放置在搜索空间中
- 对每只海豚,计算其位置的质量(适应度)
- 将所有备选方案的累积适应度初始化为零
基本优化循环:
重复指定次数的迭代:
阶段 1. 计算预定概率
计算当前迭代中保持最佳位置的概率。在过程开始时,该概率等于初始值,然后根据幂函数逐渐增加,到优化结束时达到 1。
阶段 2. 计算动态自适应系数
计算决定局部搜索与全局搜索之间平衡的系数。该系数等于最优解与最差解之差除以所有解与最优解偏差之和。当种群多样化时,系数较高;当种群收敛时,系数较低。
阶段 3. 累积适应度信息
对每只海豚:
- 相对于当前种群中的范围,对其适应度进行归一化。
- 对每个坐标,找到最接近海豚位置的备选方案
- 在该备选方案的回声定位半径内传播质量信息
- 影响强度随距中心的距离线性递减
- 在空间边界处应用反射以保留信息
- 向所有累积适应度添加一个小值以避免零概率
步骤 4. 重置最佳位置的信息
找到具有最优解的海豚,并将其坐标对应备选方案的累积适应度重置。这防止搜索过于集中在一个点上。
步骤 5. 选择新位置
对每只海豚及其每个坐标:
- 如果这是最佳海豚且有机会保留位置,则保持坐标不变
- 否则,如果有累积的适应度信息,则使用轮盘赌法按比例选择新的备选方案
- 如果累积信息缺失或不足:
- 以等于动态系数的概率,在回声定位半径内执行局部搜索
- 否则,通过选择随机备选方案执行全局搜索
- 应用搜索空间约束和离散化
阶段6. 评估新位置
计算每只海豚在新位置的解的质量。
阶段 7. 全局信息更新
更新种群中最优解和最差解的记录。如果找到的解优于当前全局最优解,则应保留。
结束:
完成所有迭代后,返回找到的最优解及其质量。
现在让我们着手实现 DEA。
让我们编写 S_Alternative 结构体。它存储优化决策过程中一个备选方案的信息,包含 "value"(表征该备选方案的值,即有效性评估)和 "AF"—— 该备选方案的累积适应度,这在需要应用迭代过程时评估备选方案质量是必要的。
//———————————————————————————————————————————————————————————————————— struct S_Alternative { double value; // alternative value double AF; // accumulated fitness for this alternative }; //————————————————————————————————————————————————————————————————————
S_Coordinate 结构体用于表示与特定坐标关联的一组备选方案,"alt" 是备选方案数组,每个对应一个坐标,"count" 是一个变量,表示 "alt" 数组中实际存储的当前备选方案数量。
//———————————————————————————————————————————————————————————————————— struct S_Coordinate { S_Alternative alt []; // array of alternatives for a given coordinate int count; // number of alternatives }; //————————————————————————————————————————————————————————————————————
接下来,我们描述直接实现优化算法(DEA)的类。C_AO_DEA 类是 C_AO 基类的继承者。创建类对象时,初始化算法的主要参数:
- popSize —— 初始化种群大小("海豚" 或位置的数量)。
- Re —— 有效搜索半径的初始值。
- Power —— 收敛曲线的幂次。
- PP1—— 初始化第一次迭代的收敛系数。
SetParams 方法用于根据 params 数组中写入的值更新算法的内部参数。提取 popSize、Re、Power、PP1 值后,执行验证检查。
方法:
- Init用于初始化算法,接受每个参数的最小值、最大值和步长,以及总世代数(迭代次数)。
- Moving负责在搜索空间中移动 "海豚";这是主优化循环的一部分。
- Revision在移动后调整种群位置和参数。
以下字段在内部用于运行算法,从类外部不可访问。
- PP是一个浮点数,表示当前迭代的预定概率,用于随机决策,currentEpoch是跟踪算法当前世代(迭代)的值。
- totalEpochs是存储计划总世代数的值。
- coeffA是用于选择位置的动态系数。
- coordData是结构体数组,其中每个 S_Coordinate 包含特定坐标的备选方案数组及其数量。
- CalculatePP用于计算当前迭代的 PP 值(预定概率)。
- CalculateAccumulativeFitness计算每个备选方案的累积适应度。
- ResetAFforBestLocation重置最佳位置的累积适应度值。
- SelectNextLocations根据当前状态为海豚选择下一个位置。
- FindNearestAlternative根据给定的坐标和值找到最近的备选方案。
- CalculateCoefficientA计算 coeffA 动态系数。
总的来说,C_AO_DEA 类已准备好用于在多维空间中寻找解。它具有用于初始化和执行主要优化步骤的公共方法,以及实现算法内部逻辑的私有方法和数据。
//———————————————————————————————————————————————————————————————————— class C_AO_DEA : public C_AO { public: //---------------------------------------------------------- ~C_AO_DEA () { } C_AO_DEA () { ao_name = "DEA"; ao_desc = "Dolphin Echolocation Algorithm"; ao_link = "https://www.mql5.com/en/articles/18495"; popSize = 100; // NL - number of locations (dolphins) Re = 2; // effective search radius Power = 2.0; // convergence curve power PP1 = 1.0; // first iteration convergence factor ArrayResize (params, 4); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "Re"; params [1].val = Re; params [2].name = "Power"; params [2].val = Power; params [3].name = "PP1"; params [3].val = PP1; } void SetParams () { popSize = (int)params [0].val; Re = (int)params [1].val; Power = params [2].val; PP1 = params [3].val; // Check the parameters validity if (Re < 0) Re = 0; if (PP1 < 0.0) PP1 = 0.0; if (PP1 > 1.0) PP1 = 1.0; if (Power < 0.1) Power = 0.1; } bool Init (const double &rangeMinP [], // minimum values const double &rangeMaxP [], // maximum values const double &rangeStepP [], // step change const int epochsP = 0); // number of epochs void Moving (); void Revision (); //------------------------------------------------------------------ int Re; // effective search radius double Power; // convergence curve power double PP1; // first iteration convergence factor private: //--------------------------------------------------------- double PP; // predefined probability for the current iteration int currentEpoch; // current epoch int totalEpochs; // total number of epochs double coeffA; // dynamic coefficient used to select positions S_Coordinate coordData []; // coordinate data (alternatives and AF) void CalculatePP (); void CalculateAccumulativeFitness (); void ResetAFforBestLocation (); void SelectNextLocations (); int FindNearestAlternative (int coord, double value); void CalculateCoefficientA (); }; //————————————————————————————————————————————————————————————————————
C_AO_DEA 类的 Init 方法用于初始化 DEA 算法。它接受每个变量的最小值和最大值、每个变量的变化步长,以及优化的总世代数作为输入。该方法首先调用基类的 StandardInit 方法来执行算法通用参数的标准初始化,如果此初始化失败,则返回 'false'。然后初始化跟踪算法执行的当前世代和总世代的变量。
之后,创建 coordData 数据结构,用于存储搜索空间每个坐标(变量)的信息。对每个坐标,确定可能的备选值数量。如果为某个坐标指定了步长变化,则根据边界和步长计算备选方案数量。如果未指定步长(等于零),则假定该坐标是连续的,并为其设置固定数量的备选方案。
然后检查 Re 参数(搜索半径),必要时进行调整,使其不超过每个坐标备选方案数量的四分之一。之后,分配内存以存储每个坐标的备选值。
最后,循环填充每个坐标的备选值数组,在最小值和最大值之间均匀分布。如果指定了步长,则使用该步长计算备选值。如果未指定步长,则使用指定边界之间的空间离散化。同时,对每个备选方案,将关联的 AF(累积适应度)值初始化为零。如果所有初始化步骤都成功完成,该方法返回 'true'。
//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_DEA::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ currentEpoch = 0; totalEpochs = epochsP; // Initialize the data structure for coordinates ArrayResize (coordData, coords); // Create alternatives for each coordinate for (int c = 0; c < coords; c++) { if (rangeStep [c] != 0) { coordData [c].count = (int)((rangeMax [c] - rangeMin [c]) / rangeStep [c]) + 1; } else { coordData [c].count = 500; } // Check that Re is not too large for the number of alternatives if (Re > coordData [c].count / 4) Re = coordData [c].count / 4; ArrayResize (coordData [c].alt, coordData [c].count); // Fill in the alternatives for (int i = 0; i < coordData [c].count; i++) { if (rangeStep [c] != 0) { coordData [c].alt [i].value = rangeMin [c] + i * rangeStep [c]; } else { coordData [c].alt [i].value = rangeMin [c] + (rangeMax [c] - rangeMin [c]) * i / (coordData [c].count - 1); } coordData [c].alt [i].AF = 0.0; } } return true; } //————————————————————————————————————————————————————————————————————
Moving 方法实现了 DEA 算法的一个主要步骤,对应于优化过程的某次迭代。如果通过 "revision" 标志检测到这是第一次迭代,则进行种群坐标的初始随机分配。对每个个体(种群元素)以及范围内的每个变量,分配随机值。这些值会根据边界和变化步长进行调整,以确保解的可接受性。之后,设置标志,初始化完成,方法退出该循环。
初始化完成后,当前世代(迭代)计数器加一。然后执行算法的关键步骤:确定当前种群中每个解的性能指标,这些指标表明它们对问题的适应程度。计算 "a" 系数。基于质量指标,确定每个解适应度的广义度量,并对最佳位置对应的 AF 信息进行重置,这使得搜索能够集中在最优解的区域。基于当前信息和计算出的系数,选择下一个位置,用于在搜索空间中更新和移动候选解。
总的来说,Moving 方法实现了 DEA 算法的一个迭代周期,在寻找最优解的过程中依次执行更新和改进解的关键步骤。
//———————————————————————————————————————————————————————————————————— //--- The main step of the algorithm (according to Algorithm 1) void C_AO_DEA::Moving () { // Initial setup if (!revision) { for (int p = 0; p < popSize; p++) { for (int c = 0; c < coords; c++) { a [p].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [p].c [c] = u.SeInDiSp (a [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); a [p].cB [c] = a [p].c [c]; } } revision = true; return; } // Increase the epoch counter currentEpoch++; // Steps of the DEA algorithm according to Algorithm 1: // 1. Calculate PP for the current iteration CalculatePP (); // 2. We calculate the 'a' dynamic coefficient CalculateCoefficientA (); // 3. Calculate accumulated fitness CalculateAccumulativeFitness (); // 4. Find the best location and reset AF for it ResetAFforBestLocation (); // 5. Select the next positions SelectNextLocations (); } //————————————————————————————————————————————————————————————————————
CalculatePP 方法计算预定概率(PP),该概率用于算法内部的决策过程。如果总世代数(迭代数)等于或小于 1,则将概率设置为等于预定的初始值(PP1)。在这种情况下,无需进一步计算,方法终止。
如果世代数大于 1,则根据给定公式计算 PP 值,该公式考虑了当前世代,并包含以下逻辑:计算幂函数值,即当前世代的 Power 次方减 1,同样计算总世代数的幂函数值。
PP 值使用一个逐渐趋近于 1 的公式进行更新,同时考虑了当前世代的进度。具体来说,向初始 PP1 值添加了与进度成比例的部分,该进度由次数以及给定的 Power 和 totalEpochs 参数进行调整。如果总指数不为零,则进行除法运算以获得当前概率;否则,该值保持等于初始 PP1。
最终,该方法在算法执行过程中动态改变概率值,确保随着世代推进,在初始值和更 "进取" 的值之间保持平衡。
//———————————————————————————————————————————————————————————————————— //--- Calculate the predetermined probability (according to the formula 5) void C_AO_DEA::CalculatePP () { if (totalEpochs <= 1) { PP = PP1; return; } // PP = PP1 + (1 - PP1) * ((Loop^Power - 1) / (LoopsNumber^Power - 1)) double iterPower = MathPow ((double)currentEpoch, Power) - 1.0; double totalPower = MathPow ((double)totalEpochs, Power) - 1.0; if (totalPower != 0) { PP = PP1 + (1.0 - PP1) * iterPower / totalPower; } else { PP = PP1; } } //————————————————————————————————————————————————————————————————————
CalculateCoefficientA 方法用于计算调节搜索过程的动态系数 ‘a’。该方法遍历当前解的整个种群,对每个解计算最大可能适应度 fB 与特定解当前适应度(a [i].f)之间的差值。这些差值累加到一个总和中。
在累加值的总和之后,'a' 系数通过 fB 与最小适应度 fW 之差除以所得总和得到。这使我们能够定义一个动态系数,它根据种群的当前状态进行自适应,并有助于平衡搜索空间的收敛速度或探索程度。因此,该方法创建了一个缩放参数,该参数考虑了解的适应度分布,并影响它们在算法后续迭代中的更新和移动策略。
//———————————————————————————————————————————————————————————————————— //--- Calculate the dynamic 'a' coefficient void C_AO_DEA::CalculateCoefficientA () { double sumFitness = 0.0; for (int i = 0; i < popSize; i++) { sumFitness += fB - a [i].f; } coeffA = (fB - fW) / sumFitness; } //————————————————————————————————————————————————————————————————————
FindNearestAlternative 方法用于在特定坐标内找到与给定值最接近的备选方案,它接受两个参数:"coord" 坐标索引和我们需要为其找到最近备选方案的 "value" 值。初始化并设置 "nearest"(最近备选方案的索引)和 "minDist"(最小距离)的初始值。
循环遍历与给定 coord 坐标关联的所有备选方案。对每个备选方案,计算给定 "value" 与备选方案值(coordData [coord].alt [i].value)之间的距离,如果计算出的距离小于当前最小距离(minDist),则用新的更小距离值更新 "minDist",并用当前备选方案的索引更新 "nearest"。
循环完成后,返回在给定坐标内与给定 "value" 最接近的备选方案的索引。因此,该方法确定指定坐标的可用备选方案中,哪一个最接近给定的实数值。
//———————————————————————————————————————————————————————————————————— //--- Search for the closest alternative int C_AO_DEA::FindNearestAlternative (int coord, double value) { int nearest = 0; double minDist = DBL_MAX; for (int i = 0; i < coordData [coord].count; i++) { double dist = MathAbs (value - coordData [coord].alt [i].value); if (dist < minDist) { minDist = dist; nearest = i; } } return nearest; } //————————————————————————————————————————————————————————————————————
CalculateAccumulativeFitness 方法用于根据算法 2 计算备选方案的累积适应度(AF)。在开始计算之前,清除每个坐标中每个备选方案的所有累积适应度值并设置为零。找到当前解种群中的适应度范围,计算为最大可能适应度(fB)与最小适应度(fW)之差。
然后,对每个代理(海豚),将其适应度相对于范围进行归一化,并对每个搜索坐标,确定最近的备选方案,在该备选方案周围半径 "Re" 内,考虑边界的反射特性,更新备选方案的累积适应度。这是通过加权累加贡献来实现的,其中权重基于距最近备选方案的距离(考虑反射边界)形成,并包含与半径 "Re" 中当前步长相关的系数。
作为该方法运行的结果,每个坐标中每个备选方案的累积适应度值得以形成,同时考虑了邻近备选方案的贡献。
//———————————————————————————————————————————————————————————————————— //--- Calculate the accumulated fitness (according to Algorithm 2) void C_AO_DEA::CalculateAccumulativeFitness () { // Clear the accumulated fitness for all alternatives for (int c = 0; c < coords; c++) { for (int i = 0; i < coordData [c].count; i++) { coordData [c].alt [i].AF = 0.0; } } double rangeFF = fB - fW; if (rangeFF == 0.0) rangeFF = DBL_EPSILON; // For each agent (dolphin) for (int i = 0; i < popSize; i++) { // Normalize 'fitness' for this agent double normalizedFitness = (a [i].f - fW) / rangeFF; for (int c = 0; c < coords; c++) { // Find the closest alternative for the current coordinate int nearestAlt = FindNearestAlternative (c, a [i].c [c]); // Update the accumulated fitness in Re radius // According to Algorithm 2: AF(A+k)j = (1/Re) * (Re - |k|) * fitness(i) + AF(A+k)j for (int k = -Re; k <= Re; k++) { int altIndex = nearestAlt + k; // Boundary check taking into account reflection (reflective characteristic) if (altIndex < 0) { altIndex = -altIndex; // reflection from the lower border } else if (altIndex >= coordData [c].count) { altIndex = 2 * (coordData [c].count - 1) - altIndex; // reflection from the upper border } if (altIndex >= 0 && altIndex < coordData [c].count) { double weight = (1.0 / (double)(Re + 1)) * (Re - MathAbs (k) + 1); coordData [c].alt [altIndex].AF += weight * normalizedFitness; } } } } // Add a small epsilon value to all AFs to avoid zero probabilities double epsilon = 0.0001; for (int c = 0; c < coords; c++) { for (int i = 0; i < coordData [c].count; i++) { coordData [c].alt [i].AF += epsilon; } } } //————————————————————————————————————————————————————————————————————
ResetAFforBestLocation 方法用于重置与种群中最优解(代理)位置对应的备选方案的累积适应度(AF)。首先,该方法确定当前种群中最优解(代理)的索引。它遍历所有解,找到适应度值最大的解。具有最高适应度的解的索引存储在 bestIndex 变量中。
对最优解的每个 'c' 坐标,该方法使用 FindNearestAlternative 函数确定与最优解在该坐标上的值最接近的备选方案,如果找到的备选方案存在(索引在可接受范围内),则将该特定备选方案的累积适应度值 AF 重置为零。
因此,该方法仅对与最优解坐标最匹配的那些备选方案重置 AF。这样做是为了减少这些备选方案对搜索后续阶段的影响,有可能促进对搜索空间其他区域的探索。
结果是,与最优解最匹配的那些坐标的适应度被 "清零 / 重置",以刺激寻找其他可能更优的解。
//———————————————————————————————————————————————————————————————————— //--- Reset AF for the best location (according to Algorithm 3) void C_AO_DEA::ResetAFforBestLocation () { // Find the index of the best solution int bestIndex = 0; double bestFitness = a [0].f; // Search for a solution with maximum fitness (since we always maximize normalized fitness) for (int i = 1; i < popSize; i++) { if (a [i].f > bestFitness) { bestFitness = a [i].f; bestIndex = i; } } // Reset AF for ALL alternatives corresponding to the coordinates of the best solution for (int c = 0; c < coords; c++) { // Find the closest alternative to the coordinate of the best solution int nearestAlt = FindNearestAlternative (c, a [bestIndex].c [c]); // Reset AF only for this alternative if (nearestAlt >= 0 && nearestAlt < coordData [c].count) { coordData [c].alt [nearestAlt].AF = 0.0; } } } //————————————————————————————————————————————————————————————————————
SelectNextLocations 方法用于基于概率考量,并同时考虑累积适应度(AF)和保持最优位置的策略,为种群中的每个解(每只海豚)选择下一个位置。该方法首先根据适应度值确定当前种群中的最优解。最优解的索引被保存下来供后续使用。
对每个解及其每个坐标,执行以下操作:如果当前解是最优解,且随机数小于 PP 概率,则保持该解的当前坐标不变。这保留了之前的最优解。然而,如果当前解不是最优解,或者在 PP 策略下位置未被保留,则对当前坐标的备选方案的所有 AF 值进行求和。如果 AF 总和大于一个小数(ε),表明存在非零适应度值,则执行轮盘赌:选择一个与 AF 总和成比例的随机数,并根据备选方案的累积 AF 总和来选择解的坐标,从而使解向适应度更高的位置移动。
如果 AF 总和接近零(所有 AF 值都非常小),则在随机数小于 coeffA 动态系数的情况下执行局部搜索。在这种情况下,选择相对于当前值的随机偏移(-Re...+Re),并用最接近的值更新坐标。同时考虑边界。
如果随机数大于 coeffA,则执行全局搜索(随机选择)。在这种情况下,为坐标选择一个完全随机的备选方案。在方法的最后,将获得的坐标值限制在允许范围(rangeMin, rangeMax)内,并用给定的 rangeStep 步长进行离散化,以确保该值在允许范围内并对应于允许值。
该方法运行的结果是,每个解的坐标都基于累积适应度的概率权重、PP(保持最优)策略以及动态的局部和全局搜索进行了更新,这使得算法能够有效地探索搜索空间并找到最优解。
//———————————————————————————————————————————————————————————————————— //--- Select next positions based on probabilities void C_AO_DEA::SelectNextLocations () { // First we find the index of the best solution int bestIndex = 0; double bestFitness = a [0].f; for (int i = 1; i < popSize; i++) { if (a [i].f > bestFitness) { bestFitness = a [i].f; bestIndex = i; } } for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { // For the best agent, apply PP if (i == bestIndex && u.RNDprobab () < PP) { // Save the current value of the coordinate of the best solution with PP probability continue; } // Select based on accumulated fitness double totalAF = 0.0; for (int alt = 0; alt < coordData [c].count; alt++) { totalAF += coordData [c].alt [alt].AF; } if (totalAF > DBL_EPSILON) // Check that there are non-zero AFs { // Choose an alternative based on roulette double rnd = u.RNDprobab () * totalAF; double cumSum = 0.0; for (int alt = 0; alt < coordData [c].count; alt++) { cumSum += coordData [c].alt [alt].AF; if (cumSum >= rnd) { a [i].c [c] = coordData [c].alt [alt].value; break; } } } else { // If all AFs are almost zero, use random selection // with the dynamic coeffA coefficient for the local search probability if (u.RNDprobab () < coeffA) // Use the dynamic coefficient { // Local search - stay close to the current position int currentAlt = FindNearestAlternative (c, a [i].c [c]); int shift = u.RNDminusOne (2 * Re + 1) - Re; // random offset within Re int newAlt = currentAlt + shift; // Check boundaries if (newAlt < 0) newAlt = 0; if (newAlt >= coordData [c].count) newAlt = coordData [c].count - 1; a [i].c [c] = coordData [c].alt [newAlt].value; } else { // Global search - completely random selection int randAlt = u.RNDminusOne (coordData [c].count); a [i].c [c] = coordData [c].alt [randAlt].value; } } // Bounds checking and discretization a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } //————————————————————————————————————————————————————————————————————
Revision 方法用于更新当前种群中最优解和最差解的信息。确定最优解的索引,并将存储最差适应度的变量赋值为当前最优解的值。这为在当前种群中寻找新的最差值创建了初始条件。搜索当前种群中的所有解:确定具有最大适应度值的解(最优解)。保存其值并更新存储的索引。我们还搜索具有最小适应度值的解(最差解)。如果确实找到了最优解,则将其坐标复制到专门用于存储当前最优解的数组或变量中。
该方法执行的结果是,在当前时刻精确知晓种群中最优解和最差解的坐标。这使算法能够跟踪搜索动态,并在决策中使用这些数据,以更好地找到最优解。
//———————————————————————————————————————————————————————————————————— //--- Update the best solution void C_AO_DEA::Revision () { int bestIND = -1; fW = fB; // Find the best and worst solutions in the current population for (int i = 0; i < popSize; i++) { // Update the best solution if (a [i].f > fB) { fB = a [i].f; bestIND = i; } // Update the worst solution if (a [i].f < fW) { fW = a [i].f; } } // Copy the coordinates of the best solution if (bestIND != -1) { ArrayCopy (cB, a [bestIND].c, 0, 0, WHOLE_ARRAY); } } //————————————————————————————————————————————————————————————————————
测试结果
现在让我们看看结果。可以立即注意到,该算法能够很好且快速地处理各种类型的任务。
DEA|海豚回声定位算法|100.0|2.0|2.0|1.0|
=============================5 Hilly's;函数运行次数:10000;结果:0.7599517883429889
25 Hilly's;函数运行次数:10000;结果:0.6757192867862007
500 Hilly's;函数运行次数:10000;结果:0.34170057553968197
=============================
5 Forest's;函数运行次数:10000;结果:0.8958173952258406
25 Forest's;函数运行次数:10000;结果:0.6422393144820473
500 Forest's;函数运行次数:10000;结果:0.23940903266305935
=============================
5 Megacity's;函数运行次数:10000;结果:0.6153846153846154
25 Megacity's;函数运行次数:10000;结果:0.4403076923076923
500 Megacity's;函数运行次数:10000;结果:0.15115384615384736
=============================
总得分:4.76168 (52.91%)
可视化结果显示,低维函数(绿色)的结果分布较分散,而中维函数(蓝色)的结果表现良好。

海豚回声定位算法(DEA)在 Hilly 函数上的表现

海豚回声定位算法(DEA)在 Forest 函数上的表现

海豚回声定位算法(DEA)在 Megacity 函数上的表现
根据测试结果,DEA 算法在排名表中位列第 25 名。
| # | 算法 | 说明 | Hilly | Hilly 最终结果 | Forest | Forest 最终结果 | Megacity (离散) | Megacity 最终结果 | 最终 结果 | % of MAX | ||||||
| 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | ||||||||
| 1 | ANS | 交叉邻近搜索 | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| 2 | CLA | 代码锁定算法(JOO) | 0.95345 | 0.87107 | 0.37590 | 2.20042 | 0.98942 | 0.91709 | 0.31642 | 2.22294 | 0.79692 | 0.69385 | 0.19303 | 1.68380 | 6.107 | 67.86 |
| 3 | AMOm | 动物迁徙优化M | 0.90358 | 0.84317 | 0.46284 | 2.20959 | 0.99001 | 0.92436 | 0.46598 | 2.38034 | 0.56769 | 0.59132 | 0.23773 | 1.39675 | 5.987 | 66.52 |
| 4 | (P+O)ES | (P+O) 进化策略 | 0.92256 | 0.88101 | 0.40021 | 2.20379 | 0.97750 | 0.87490 | 0.31945 | 2.17185 | 0.67385 | 0.62985 | 0.18634 | 1.49003 | 5.866 | 65.17 |
| 5 | CTA | 彗尾算法(JOO) | 0.95346 | 0.86319 | 0.27770 | 2.09435 | 0.99794 | 0.85740 | 0.33949 | 2.19484 | 0.88769 | 0.56431 | 0.10512 | 1.55712 | 5.846 | 64.96 |
| 6 | TETA | 时间演化旅行算法(JOO) | 0.91362 | 0.82349 | 0.31990 | 2.05701 | 0.97096 | 0.89532 | 0.29324 | 2.15952 | 0.73462 | 0.68569 | 0.16021 | 1.58052 | 5.797 | 64.41 |
| 7 | SDSm | 随机扩散搜索 M | 0.93066 | 0.85445 | 0.39476 | 2.17988 | 0.99983 | 0.89244 | 0.19619 | 2.08846 | 0.72333 | 0.61100 | 0.10670 | 1.44103 | 5.709 | 63.44 |
| 8 | BOAm | 台球优化算法 M | 0.95757 | 0.82599 | 0.25235 | 2.03590 | 1.00000 | 0.90036 | 0.30502 | 2.20538 | 0.73538 | 0.52523 | 0.09563 | 1.35625 | 5.598 | 62.19 |
| 9 | AAm | 射箭算法 M | 0.91744 | 0.70876 | 0.42160 | 2.04780 | 0.92527 | 0.75802 | 0.35328 | 2.03657 | 0.67385 | 0.55200 | 0.23738 | 1.46323 | 5.548 | 61.64 |
| 10 | ESG | 社会群体的进化(JOO) | 0.99906 | 0.79654 | 0.35056 | 2.14616 | 1.00000 | 0.82863 | 0.13102 | 1.95965 | 0.82333 | 0.55300 | 0.04725 | 1.42358 | 5.529 | 61.44 |
| 11 | SIA | 模拟各向同性退火(JOO) | 0.95784 | 0.84264 | 0.41465 | 2.21513 | 0.98239 | 0.79586 | 0.20507 | 1.98332 | 0.68667 | 0.49300 | 0.09053 | 1.27020 | 5.469 | 60.76 |
| 12 | BBO | 基于生物地理学的优化算法 | 0.94912 | 0.69456 | 0.35031 | 1.99399 | 0.93820 | 0.67365 | 0.25682 | 1.86867 | 0.74615 | 0.48277 | 0.17369 | 1.40261 | 5.265 | 58.50 |
| 13 | ACS | 人工协同搜索 | 0.75547 | 0.74744 | 0.30407 | 1.80698 | 1.00000 | 0.88861 | 0.22413 | 2.11274 | 0.69077 | 0.48185 | 0.13322 | 1.30583 | 5.226 | 58.06 |
| 14 | DA | 辩证算法 | 0.86183 | 0.70033 | 0.33724 | 1.89940 | 0.98163 | 0.72772 | 0.28718 | 1.99653 | 0.70308 | 0.45292 | 0.16367 | 1.31967 | 5.216 | 57.95 |
| 15 | BHAm | 黑洞算法 M | 0.75236 | 0.76675 | 0.34583 | 1.86493 | 0.93593 | 0.80152 | 0.27177 | 2.00923 | 0.65077 | 0.51646 | 0.15472 | 1.32195 | 5.196 | 57.73 |
| 16 | ASO | 无政府社会优化 | 0.84872 | 0.74646 | 0.31465 | 1.90983 | 0.96148 | 0.79150 | 0.23803 | 1.99101 | 0.57077 | 0.54062 | 0.16614 | 1.27752 | 5.178 | 57.54 |
| 17 | RFO | 皇家同花顺优化算法 (JOO) | 0.83361 | 0.73742 | 0.34629 | 1.91733 | 0.89424 | 0.73824 | 0.24098 | 1.87346 | 0.63154 | 0.50292 | 0.16421 | 1.29867 | 5.089 | 56.55 |
| 18 | AOSm | 原子轨道搜索 M | 0.80232 | 0.70449 | 0.31021 | 1.81702 | 0.85660 | 0.69451 | 0.21996 | 1.77107 | 0.74615 | 0.52862 | 0.14358 | 1.41835 | 5.006 | 55.63 |
| 19 | TSEA | 龟壳进化算法(JOO) | 0.96798 | 0.64480 | 0.29672 | 1.90949 | 0.99449 | 0.61981 | 0.22708 | 1.84139 | 0.69077 | 0.42646 | 0.13598 | 1.25322 | 5.004 | 55.60 |
| 20 | DE | 差分进化 | 0.95044 | 0.61674 | 0.30308 | 1.87026 | 0.95317 | 0.78896 | 0.16652 | 1.90865 | 0.78667 | 0.36033 | 0.02953 | 1.17653 | 4.955 | 55.06 |
| 21 | SRA | 成功餐饮经营者算法(joo) | 0.96883 | 0.63455 | 0.29217 | 1.89555 | 0.94637 | 0.55506 | 0.19124 | 1.69267 | 0.74923 | 0.44031 | 0.12526 | 1.31480 | 4.903 | 54.48 |
| 22 | CRO | 化学反应优化 | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| 23 | BIO | 血缘遗传优化算法 (JOO) | 0.81568 | 0.65336 | 0.30877 | 1.77781 | 0.89937 | 0.65319 | 0.21760 | 1.77016 | 0.67846 | 0.47631 | 0.13902 | 1.29378 | 4.842 | 53.80 |
| 24 | BSA | 鸟群算法 | 0.89306 | 0.64900 | 0.26250 | 1.80455 | 0.92420 | 0.71121 | 0.24939 | 1.88479 | 0.69385 | 0.32615 | 0.10012 | 1.12012 | 4.809 | 53.44 |
| 25 | DEA | 海豚回声定位算法 | 0.75995 | 0.67572 | 0.34171 | 1.77738 | 0.89582 | 0.64223 | 0.23941 | 1.77746 | 0.61538 | 0.44031 | 0.15115 | 1.20684 | 4.762 | 52.91 |
| 26 | HS | 和声搜索算法 | 0.86509 | 0.68782 | 0.32527 | 1.87818 | 0.99999 | 0.68002 | 0.09590 | 1.77592 | 0.62000 | 0.42267 | 0.05458 | 1.09725 | 4.751 | 52.79 |
| 27 | SSG | 树苗播种和生长算法 | 0.77839 | 0.64925 | 0.39543 | 1.82308 | 0.85973 | 0.62467 | 0.17429 | 1.65869 | 0.64667 | 0.44133 | 0.10598 | 1.19398 | 4.676 | 51.95 |
| 28 | BCOm | 细菌趋化优化算法M | 0.75953 | 0.62268 | 0.31483 | 1.69704 | 0.89378 | 0.61339 | 0.22542 | 1.73259 | 0.65385 | 0.42092 | 0.14435 | 1.21912 | 4.649 | 51.65 |
| 29 | ABO | 非洲水牛优化 | 0.83337 | 0.62247 | 0.29964 | 1.75548 | 0.92170 | 0.58618 | 0.19723 | 1.70511 | 0.61000 | 0.43154 | 0.13225 | 1.17378 | 4.634 | 51.49 |
| 30 | (PO)ES | (PO) 进化策略 | 0.79025 | 0.62647 | 0.42935 | 1.84606 | 0.87616 | 0.60943 | 0.19591 | 1.68151 | 0.59000 | 0.37933 | 0.11322 | 1.08255 | 4.610 | 51.22 |
| 31 | FBA | 基于分形的算法 | 0.79000 | 0.65134 | 0.28965 | 1.73099 | 0.87158 | 0.56823 | 0.18877 | 1.62858 | 0.61077 | 0.46062 | 0.12398 | 1.19537 | 4.555 | 50.61 |
| 32 | TSm | 禁忌搜索优化算法 | 0.87795 | 0.61431 | 0.29104 | 1.78330 | 0.92885 | 0.51844 | 0.19054 | 1.63783 | 0.61077 | 0.38215 | 0.12157 | 1.11449 | 4.536 | 50.40 |
| 33 | BSO | 头脑风暴优化 | 0.93736 | 0.57616 | 0.29688 | 1.81041 | 0.93131 | 0.55866 | 0.23537 | 1.72534 | 0.55231 | 0.29077 | 0.11914 | 0.96222 | 4.498 | 49.98 |
| 34 | WOAm | 鲸鱼优化算法M | 0.84521 | 0.56298 | 0.26263 | 1.67081 | 0.93100 | 0.52278 | 0.16365 | 1.61743 | 0.66308 | 0.41138 | 0.11357 | 1.18803 | 4.476 | 49.74 |
| 35 | AEFA | 人工电场算法 | 0.87700 | 0.61753 | 0.25235 | 1.74688 | 0.92729 | 0.72698 | 0.18064 | 1.83490 | 0.66615 | 0.11631 | 0.09508 | 0.87754 | 4.459 | 49.55 |
| 36 | AEO | 基于人工生态系统的优化算法 | 0.91380 | 0.46713 | 0.26470 | 1.64563 | 0.90223 | 0.43705 | 0.21400 | 1.55327 | 0.66154 | 0.30800 | 0.28563 | 1.25517 | 4.454 | 49.49 |
| 37 | CAm | 骆驼算法 M | 0.78684 | 0.56042 | 0.35133 | 1.69859 | 0.82772 | 0.56041 | 0.24336 | 1.63149 | 0.64846 | 0.33092 | 0.13418 | 1.11356 | 4.444 | 49.37 |
| 38 | ACOm | 蚁群优化 M | 0.88190 | 0.66127 | 0.30377 | 1.84693 | 0.85873 | 0.58680 | 0.15051 | 1.59604 | 0.59667 | 0.37333 | 0.02472 | 0.99472 | 4.438 | 49.31 |
| 39 | CMAES | covariance_matrix_adaptation_evolution_strategy | 0.76258 | 0.72089 | 0.00000 | 1.48347 | 0.82056 | 0.79616 | 0.00000 | 1.61672 | 0.75846 | 0.49077 | 0.00000 | 1.24923 | 4.349 | 48.33 |
| 40 | BFO-GA | 细菌觅食优化 - ga | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| 41 | SOA | 简单优化算法 | 0.91520 | 0.46976 | 0.27089 | 1.65585 | 0.89675 | 0.37401 | 0.16984 | 1.44060 | 0.69538 | 0.28031 | 0.10852 | 1.08422 | 4.181 | 46.45 |
| 42 | ABHA | 人工蜂巢算法 | 0.84131 | 0.54227 | 0.26304 | 1.64663 | 0.87858 | 0.47779 | 0.17181 | 1.52818 | 0.50923 | 0.33877 | 0.10397 | 0.95197 | 4.127 | 45.85 |
| 43 | ACMO | 大气云层模型优化 | 0.90321 | 0.48546 | 0.30403 | 1.69270 | 0.80268 | 0.37857 | 0.19178 | 1.37303 | 0.62308 | 0.24400 | 0.10795 | 0.97503 | 4.041 | 44.90 |
| 44 | ADAMm | 自适应动量评估 M | 0.88635 | 0.44766 | 0.26613 | 1.60014 | 0.84497 | 0.38493 | 0.16889 | 1.39880 | 0.66154 | 0.27046 | 0.10594 | 1.03794 | 4.037 | 44.85 |
| 45 | CGO | 混沌博弈优化算法 | 0.57256 | 0.37158 | 0.32018 | 1.26432 | 0.61176 | 0.61931 | 0.62161 | 1.85267 | 0.37538 | 0.21923 | 0.19028 | 0.78490 | 3.902 | 43.35 |
| RW | 随机游走 | 0.48754 | 0.32159 | 0.25781 | 1.06694 | 0.37554 | 0.21944 | 0.15877 | 0.75375 | 0.27969 | 0.14917 | 0.09847 | 0.52734 | 2.348 | 26.09 | |
总结
该算法的主要优势包括:自适应搜索控制:预定概率逐渐增加,将平衡从探索转向开发; 动态自适应;集体记忆:累积适应度保存并传播关于有前景区域的信息;多样性机制:对最佳位置的信息重置刺激了对新区域的探索。
该算法的优势在于其自适应性。动态系数使算法能够对种群状态做出响应,并适应任务的节奏。当海豚分散在搜索空间中时,算法鼓励局部探索。当它们开始向目标收敛时,算法推动它们走向新的视野,防止它们困在局部成功的错觉中。
累积适应度是种群的集体记忆,每一次发现都会在解空间中留下回声。但与简单的累积不同,该算法能够遗忘 —— 将最佳位置清零反而会带来更好的发现,这看似矛盾却真实有效。
这不仅仅是一个比喻 —— 这是一种优化哲学,其中回声定位器的每一次脉冲声都承载着关于可能性空间的信息。

图 2. 根据相应测试结果的算法颜色分级

图 3. 算法测试结果直方图(比例从 0 到 100,越高越好,其中 100 是最大可能的理论结果,存档中有一个用于计算评级表的脚本)
DEA算法的优缺点:
优点:
- 在中维和高维函数上收敛性良好。
- 外部参数数量少。
缺点:
- 在低维问题上存在一定的陷入局部最优的倾向。
- 高维函数上资源消耗较大。
本文附带了一个压缩包,其中包含了算法代码的当前版本。作者不对文中经典算法描述的绝对准确性承担责任。其中许多算法均经过修改,以提升搜索能力。文章中表述的结论和论断是基于实验的结果。
本文使用的程序文件
| # | 名称 | 类型 | 说明 |
|---|---|---|---|
| 1 | #C_AO.mqh | 库 | 种群优化算法的父类 |
| 2 | #C_AO_enum.mqh | 库 | 群体优化算法枚举 |
| 3 | TestFunctions.mqh | 库 | 测试函数库 |
| 4 | TestStandFunctions.mqh | 库 | 测试台函数库 |
| 5 | Utilities.mqh | 库 | 辅助函数库 |
| 6 | CalculationTestResults.mqh | 库 | 用于计算对比表中结果的脚本 |
| 7 | Testing AOs.mq5 | 脚本 | 所有种群优化算法的统一测试平台 |
| 8 | Simple use of population optimization algorithms.mq5 | 脚本 | 一个不包含可视化功能的种群优化算法使用示例 |
| 9 | Test_AO_DEA.mq5 | 脚本 | DEA 测试程序 |
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/18495
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(终篇)
您应该了解的MQL5向导技巧(第七十四部分):结合监督学习运用一目均衡表与ADX-Wilder形态
神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(DA-CG-LSTM)