中心引力优化(CFO)算法
目录
概述
大自然经过数十亿年的进化,创造了许多高效的优化机制,激励研究人员创造新的算法。引力是这些令人叹为观止的自然现象之一 —— 它是支配宇宙天体运动的基本力。我们已经多次分析过类似的算法...
中心引力优化(CFO)算法是将重力运动学原理应用于数值优化领域的尝试。这种元启发式算法基于确定性运动定律,模拟了多维解空间中虚拟粒子的相互作用,其中每个粒子在重力模拟的影响下倾向于目标函数值更好的区域。
CFO 基于一个简单直观的比喻:想象一组分布在可能解决方案空间中的试验点(探测器)。每个探测器都有一个“质量”,该质量与其所代表的解决方案的质量成正比 —— 即目标函数的值。正如巨大的天体吸引质量较小的天体一样,具有最佳解决方案的探测器也会产生一个虚拟引力场,吸引其他探测器。
每个探测器的运动都遵循类似于牛顿定律的规律,加速度由其他探测器的总吸引力决定,位移则根据运动学方程发生。该算法的一个重要特点是其确定性 —— 与许多其他元启发式方法不同,CFO 在其核心运行循环中不使用随机变量。
算法的实现
CFO 算法的历史始于研究人员的思考:如果将物理定律应用于寻找最优解会怎样?想象一下,在一个广阔的丘陵地区,每个点的高度对应于解决方案的质量。山越高,解决方案越好。我们的目标是找到最高点,但问题是我们无法一次看到整个景观。相反,我们有一组研究人员(样本),能够在该区域内移动并报告其当前位置的海拔高度。
一开始,我们的探测器随机分布在整个区域。有些探测器最终流落到低地,另一些在山坡上,有些可能足够幸运,可以直接到达小山顶。每个探测器都有自己的“重量”,与它所在位置的高度成正比。点越高,探测器“越重”。现在最有趣的部分开始了 —— 我们的探测器并非随机游荡,而是遵循万有引力定律。想象一下,“较重的”探测器(找到最佳点的探测器)会吸引“较轻的”探测器(处于最差位置的探测器)。此外,这种吸引力是单向的:好的决策会吸引坏的决策,但反之则不然。
引力是根据类似于牛顿万有引力定律的规则计算的。这取决于探测器之间的“重量”差异(解决方案质量的差异)和它们之间的距离。具有高适应度函数值的探测器强烈吸引具有低值的附近探测器,但对远处样本的影响很小。在这些力的影响下,每个探测器都加速并开始移动。小的、“轻的”探测器像球从山坡上滚到山顶一样,迅速冲向“更重的”探测器。在算法的每一步中,探测器都会重新计算吸引力并调整其运动。如果探测器试图超出已确定的搜索区域边界,则会触发反射机制 —— 想象一下,区域边缘有一堵墙,探测器会从墙边反弹回允许的区域。
随着时间的推移,探测器开始聚集在地形的高地周围。它们大多集中在最有希望的区域周围,并且随着每一次迭代,它们都能更准确地确定峰值的位置。理想情况下,如果给算法足够的时间,所有探测结果都应该收敛到全局最大值附近 —— 即整个地形的最高点。
CFO 的特殊之处在于它本质上是一个确定性算法 —— 如果你用相同的初始探测器分布运行两次,它将给出相同的结果。这使其有别于许多其他依赖随机性的元启发式算法。

图1.中心引力优化算法的流程图
图 1 显示了中心引力优化 (CFO) 算法在搜索空间中的运行原理。图中显示了目标函数景观,颜色从蓝色(低值)到黄色(高值)渐变。全局最大值(最高点)和局部最大值(最低峰值)。三个探测器(搜索代理)分别被指定为探测器 1、探测器 2 和探测器 3。吸引力(红色箭头)显示了较高点如何吸引探测器。 这是 CFO 的核心概念 —— 最好的决策会吸引最差的决策,但反之则不然。虚线表示样本达到最大值的轨迹。该过程的数学方程式包括:
引力计算:对于任意两个探测器 i 和 j,如果探测器 j 的函数值(质量)大于探测器 i 的函数值,则探测器 j 对探测器 i 施加的引力按以下公式计算:F = g × [(m_j - m_i)^α / d^β] × direction,其中:- g — 万有引力常数
- m_j 和 m_i — j 和 i 探测器的函数值(质量)
- α — 质量指数(通常为 2)
- d — 探测器之间的距离
- β — 距离指数(通常为 2)
- direction 是从探测器 i 指向探测器 j 的单位向量。
位置更新:每个探测器的位置根据以下公式更新:x_new = x_old + 0.5 × a,其中:
- x_new — 新位置
- x_old — 当前位置
- a — 加速度
该算法迭代地将这些计算应用于所有样本,逐渐将它们移向搜索空间中的最优值。随着时间的推移,样本倾向于聚集在全局和局部最大值附近,通常在全局最优值附近观察到最高浓度。
CFO 的独特性在于其确定性和单向吸引机制,它将研究引向最佳解决方案,而不涉及其基本形式的随机性。CFO 算法的伪代码:
- 初始化:
- 定义搜索空间的边界。
- 我们设定了以下参数:样本数量、引力常数、质量和距离的指数、重新定位因子。
- 创建给定数量的样本,并将其随机放置在搜索空间中。
- 对于每个样本,计算目标函数的初始值。
- 主循环(重复指定的世代数):
- 对于每个探测器:
- 将加速度矢量重置为零。
- 考虑其他探测器的影响:
- 如果另一个样本具有更好的目标函数值(更大的“质量”),就会产生吸引力。
- 计算探测器之间的距离。
- 吸引力与“质量”差的 α 次方成正比,与距离的 β 次方成反比。
- 力的方向是从当前探测器指向“较重”的探测器。
- 将所有具有最佳函数值的探测器的影响相加。
- 计算完所有加速度后,更新探测器的位置:
- 新位置 = 原位置 + 一半加速度。
- 如果探测器超出搜索空间,则进行重新定位:
- 当超出下边界时:我们将探测器反射到空间中,同时考虑重新定位因子。
- 当超过上限时:类似地,但方向相反。
- 重新计算所有探测器在新位置的目标函数值。
- 更新已找到的最佳解决方案信息。
- 对于每个探测器:
- 完成:
- 返回找到的最佳解决方案(目标函数值最大的探测器的坐标)。
接下来我们来编写算法代码,定义 S_CFO_Agent 结构,它继承自 S_AO_Agent,这意味着 S_CFO_Agent 接收 S_AO_Agent 中定义的所有属性和方法。
结构字段:a[] — 动态数组,用于存储加速度值。Init() 方法用于初始化结构体,根据传递的 coords 参数调整 c 数组的大小,并根据 coords 调整 a 加速度数组的大小。这样就可以根据坐标的数量动态设置数组大小,在使用加速度数组之前将其所有元素的值清除,并将 f 变量的值设置为 double 类型的最小可能值,以初始化适应度函数,确保计算出的任何其他值都大于当前值。
//—————————————————————————————————————————————————————————————————————————————— //--- CFO probe structure struct S_CFO_Agent : public S_AO_Agent { double a []; // acceleration vector void Init (int coords) { ArrayResize (c, coords); // coordinates ArrayResize (a, coords); // acceleration ArrayInitialize (a, 0.0); // reset accelerations f = -DBL_MAX; // fitness function value } }; //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类继承自 C_AO 类,并定义 CFO 算法。构造函数和析构函数。在这种情况下,析构函数不执行任何特定操作。C_AO_CFO() 是一个构造函数,用于初始化算法参数。它设置各种变量的值,例如:
- popSize、g、alpha、beta、initialFrep、finalFrep、noiseFactor 是与算法及其优化函数相关的参数。
- frep — 当前重定位因子,由 initialFrep 初始化。
- params 数组已初始化。它将包含算法参数。之后,它们的值会被复制到一个具有相应名称的数组中。
类方法:
- SetParams() 通过从参数数组中获取值来设置算法参数。它还将当前重新定位因子设置为 initialFrep。
- Init() 负责使用最小和最大参数值初始化算法,以及更改这些参数值的步骤。epochsP 参数指定算法执行的世代数。
- Moving() 负责在算法中移动探测器(代理)。
- Revision() 可用于审核或更新代理的状态。
类字段:
- S_CFO_Agent probe[] — 用于优化的探测器(代理)数组。
- epochs、epochNow — 私有字段,表示 世代总数和当前世代。
其他私有方法:
- InitialDistribution() — 初始化搜索空间中代理的分布。
- UpdateRepFactor() — 根据系统的当前状态更新重定位因子。
- CalculateAccelerations() — 根据代理的位置和交互计算代理的加速度。
- UpdatePositions() — 用于根据代理的加速度更新其位置。
- CalculateDistanceSquared() — 用于计算空间中两点之间的距离并评估代理之间交互的方法。
//—————————————————————————————————————————————————————————————————————————————— //--- Main class of the CFO algorithm class C_AO_CFO : public C_AO { public: //-------------------------------------------------------------------- ~C_AO_CFO () { } C_AO_CFO () { ao_name = "CFO"; ao_desc = "Central Force Optimization"; ao_link = "https://www.mql5.com/en/articles/17167"; popSize = 30; // number of probes g = 1.0; // gravitational constant alpha = 0.1; // mass power beta = 0.1; // degree for distance initialFrep = 0.9; // initial repositioning factor finalFrep = 0.1; // final repositioning factor noiseFactor = 1.0; // random noise factor frep = initialFrep; // current repositioning factor ArrayResize (params, 7); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "g"; params [1].val = g; params [2].name = "alpha"; params [2].val = alpha; params [3].name = "beta"; params [3].val = beta; params [4].name = "initialFrep"; params [4].val = initialFrep; params [5].name = "finalFrep"; params [5].val = finalFrep; params [6].name = "noiseFactor"; params [6].val = noiseFactor; } void SetParams () { popSize = (int)MathMax (1, params [0].val); g = params [1].val; alpha = params [2].val; beta = params [3].val; initialFrep = params [4].val; finalFrep = params [5].val; noiseFactor = params [6].val; frep = initialFrep; } 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 (); //---------------------------------------------------------------------------- double g; // gravitational constant double alpha; // power for mass double beta; // degree for distance double initialFrep; // initial repositioning factor double finalFrep; // final repositioning factor double noiseFactor; // random noise factor S_CFO_Agent probe []; // array of probes private: //------------------------------------------------------------------- int epochs; // total number of epochs int epochNow; // current epoch double frep; // repositioning factor void InitialDistribution (); void UpdateRepFactor (); void CalculateAccelerations (); void UpdatePositions (); double CalculateDistanceSquared (const double &x1 [], const double &x2 []); }; //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类的 Init() 方法负责初始化 CFO 算法,接受最小和最大参数值的数组、更改这些值的步长以及世代数(默认值为 0)。它调用 StandardInit 方法来检查传递的值范围的有效性。如果检查失败,该方法返回 “false”。它设置总轮数和当前世代数(0)。探测器(代理)数组的大小变为种群大小(popSize)。该方法通过调用 “probe” 数组中每个代理的 Init 方法并传递坐标来初始化每个代理。如果初始化成功,该方法返回 “true”。因此,Init 方法设置算法运行的初始参数和条件。
//—————————————————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_CFO::Init (const double &rangeMinP [], // minimum values const double &rangeMaxP [], // maximum values const double &rangeStepP [], // step change const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //---------------------------------------------------------------------------- epochs = epochsP; epochNow = 0; // Initialization of probes ArrayResize (probe, popSize); for (int i = 0; i < popSize; i++) probe [i].Init (coords); return true; } //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类的 Moving() 方法负责 CFO 算法的主要步骤。该方法首先增加当前 epoch 计数器,指示算法的下一步。如果该方法是第一次被调用,且 “revision” 等于 “false”,则执行初始初始化,之后完成执行。这是设置初始值和状态所必需的。接下来,将适应度函数的值从代理数组复制到临时数组中,以保持它们与进一步计算的相关性。
该方法更新了负责在搜索空间中重新定位代理的参数,这对算法的适应性很重要,然后该方法根据当前状态计算代理的加速度并更新其位置,从而确保代理在搜索空间内的移动。最后,该方法将新的代理位置与主数组同步,记录变化并确保数据一致性。Moving() 方法根据算法执行期间的适应度函数和当前位置,对代理的状态进行系统更新。
//—————————————————————————————————————————————————————————————————————————————— //--- The main step of the algorithm void C_AO_CFO::Moving () { epochNow++; // Initial initialization if (!revision) { InitialDistribution (); revision = true; return; } //---------------------------------------------------------------------------- // Copy the fitness function values from the base class for (int p = 0; p < popSize; p++) { probe [p].f = a [p].f; } // Update the repositioning parameter UpdateRepFactor (); // Main CFO loop CalculateAccelerations (); UpdatePositions (); // Synchronize positions with the base class for (int p = 0; p < popSize; p++) { ArrayCopy (a [p].c, probe [p].c); } } //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类的 InitialDistribution 方法负责在搜索空间中初始分配探测器(代理)。该方法遍历 popSize 群体中的每个个体。对于每个代理,通过将数组 a 设置为 0 并将适应度函数 f 设置为最小值来初始化其值。对于每个代理坐标,在指定的边界(rangeMin 和 rangeMax)内执行值的随机分布。生成随机值后,使用 SeInDiSp 方法对其进行处理,将值调整到特定范围和 rangeStep 步长。最后,将代理坐标从临时数组 c 复制到主数组 a,从而固定每个代理的初始状态。
//—————————————————————————————————————————————————————————————————————————————— //--- Initial probe distribution void C_AO_CFO::InitialDistribution () { for (int p = 0; p < popSize; p++) { ArrayInitialize (probe [p].a, 0.0); probe [p].f = -DBL_MAX; // Random distribution for (int c = 0; c < coords; c++) { probe [p].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); probe [p].c [c] = u.SeInDiSp (probe [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } ArrayCopy (a [p].c, probe [p].c); } } //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类的 UpdateRepFactor 方法负责在算法运行期间更新重定位因子(或抑制因子)。该方法首先进行检查,如果 epoch 数大于 0,则通过从 initialFrep 线性递减到 finalFrep 来计算“frep”重新定位因子的新值。这是根据当前 epochNow 在总 epoch 数内的值来完成的。如果迭代次数为 0,则 frep 保持等于 initialFrep。如果算法处于初始阶段,这确保了因子的稳定性。在该方法结束时,使用 MathMax 和 MathMin 函数将 frep 限制在 0 到 1 的范围内。这确保了重新定位因子不会超出既定的限制,这对算法的稳定性和正确操作非常重要。
//—————————————————————————————————————————————————————————————————————————————— //--- Update the repositioning factor void C_AO_CFO::UpdateRepFactor () { // Linearly decrease frep from the initial to the final value if (epochs > 0) frep = initialFrep - (initialFrep - finalFrep) * epochNow / epochs; else frep = initialFrep; // Value constraint frep = MathMax (0.0, MathMin (1.0, frep)); } //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类的 CalculateAccelerations 方法旨在根据所有其他代理的影响,计算总体中每个代理(或样本)的加速度。该方法的主要步骤和逻辑如下所述。对于群体中的每个代理(从 0 到 popSize),probe[p].a 加速度值重置为 0。这样做是为了从头开始计算,并根据与其他代理的交互来积累加速效果。对于每个代理,内部循环遍历所有其他代理(从 0 到 popSize)。如果当前 p 个代理的索引与另一个 k 个代理的索引匹配,则通过 “continue” 命令跳过该次迭代。计算两个代理适应度值的差异(massDiff = probe[k].f - probe[p].f)。该值用于确定一个代理比另一个代理 “更差”(或更好)多少。如果 massDiff 大于零,则表示索引为 k 的第二个代理比索引为 p 的当前代理具有更高的适应度。在这种情况下,将执行以下操作:
-
距离计算:使用 CalculateDistanceSquared 函数计算两个代理当前坐标之间距离的平方。如果该距离太小(小于最小正值),则跳过该次迭代。
-
确定引力的方向:如果距离大于 DBL_EPSILON,则计算实际距离。对于每个 c 坐标,确定从当前代理指向适应度更高的代理的力的方向。
-
加速度公式: 当前代理的加速度根据以下公式更新:probe [p].a [c] += g * MathPow (massDiff, alpha) * direction / MathPow (distance, beta),该公式考虑了质量差异(适应度值)、探测器之间的距离以及影响交互强度的某些比率(g、alpha、beta) 。
该方法允许每个代理考虑其他代理对其加速的影响,这是优化中的关键因素。以这种方式计算的加速度稍后将用于更新解空间中代理的位置。
//—————————————————————————————————————————————————————————————————————————————— //--- Calculate accelerations void C_AO_CFO::CalculateAccelerations () { for (int p = 0; p < popSize; p++) { // Reset the acceleration for the current probe ArrayInitialize (probe [p].a, 0.0); // Summarize the influence of all other probes for (int k = 0; k < popSize; k++) { if (k == p) continue; // Difference in masses (fitness values) double massDiff = probe [k].f - probe [p].f; // Check the condition of the U(...) unit function if (massDiff > 0) // Strict condition for the unit function { // Calculate the distance between probes double distSquared = CalculateDistanceSquared (probe [k].c, probe [p].c); if (distSquared < DBL_EPSILON) continue; double distance = MathSqrt (distSquared); for (int c = 0; c < coords; c++) { // Force direction double direction = (probe [k].c [c] - probe [p].c [c]) / distance; // Acceleration equation probe [p].a [c] += g * MathPow (massDiff, alpha) * direction / MathPow (distance, beta); } } } } } //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类的 UpdatePositions 方法用于更新解空间中代理(或样本)的位置,同时考虑其加速度、随机因素和边界约束。该方法包含以下几个步骤:
降低随机噪声因子:- 分析当前随机噪声比 currentNoiseFactor 的当前值。该比率初始值等于 noiseFactor。
- 如果 epoch 数大于 0,则该比率与 epochNow 当前 epoch 成比例减小。这意味着随着迭代次数的增加,噪声会减少,从而使算法能够逐渐更准确地找到最优解。
该方法遍历群体中的所有个体(从 0 到 popSize),并且对于每个个体,该方法遍历其所有坐标(从 0 到 coords)。使用当前加速度 probe[p].a[c] 的公式更新代理位置。在这种情况下,可以使用一个简单的公式,其中新位置等于旧位置加上当前加速度的一半。
在更新后的位置上添加一个小的随机偏移量,该偏移量取决于当前的噪声因子、重力 g 值以及 -1 到 1 范围内的随机数。 原始算法是严格确定性的,所以我决定添加一个随机性元素,我将在下面讨论。这种偏移增加了随机性,有助于避免陷入局部最小值。如果新位置超出指定的限制(存储在 rangeMin 和 rangeMax 中的最小值和最大值),则调整位置,使其保持在允许的范围内;如果指定了采样步长,则使用 SeInDiSp 方法进一步调整代理的位置,使位置变为步长的最近倍数。
//—————————————————————————————————————————————————————————————————————————————— //--- Update positions void C_AO_CFO::UpdatePositions () { // Random noise ratio decreases with increasing epoch number double currentNoiseFactor = noiseFactor; if (epochs > 0) currentNoiseFactor *= (1.0 - (double)epochNow / epochs); for (int p = 0; p < popSize; p++) { for (int c = 0; c < coords; c++) { // Update the position by equation probe [p].c [c] += 0.5 * probe [p].a [c]; // Add a small random offset directly to the position probe [p].c [c] += currentNoiseFactor * g * u.RNDfromCI (-1.0, 1.0); // Reposition when going out of bounds if (probe [p].c [c] < rangeMin [c]) probe [p].c [c] = rangeMin [c]; if (probe [p].c [c] > rangeMax [c]) probe [p].c [c] = rangeMax [c]; // Discretization if step is specified probe [p].c [c] = u.SeInDiSp (probe [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类的 CalculateDistanceSquared 方法负责计算多维空间中两点之间距离的平方。该方法用于优化,因为返回平方距离值消除了计算平方根的需要,这大大降低了计算成本。该方法接受两个参数:x1 和 x2。它们是数组(const double &x1[] 和 const double &x2[]),表示空间中两个点的坐标,其维度等于 “coord” 坐标的数量。在方法开始时,创建一个名为 “sum” 的变量,并将其初始化为 0。该变量用于累加坐标平方差之和。该方法遍历所有坐标(从 0 到 coords-1),并对每个坐标执行以下操作:
- 计算数组 x1 和 x2 的对应元素之间的差(diff = x1[i] - x2[i])。
- 计算差值的平方(diff * diff)。
- 将差值的平方加到 “sum” 变量中。
//—————————————————————————————————————————————————————————————————————————————— //--- Calculate distance (returns squared distance for optimization) double C_AO_CFO::CalculateDistanceSquared (const double &x1 [], const double &x2 []) { double sum = 0.0; for (int i = 0; i < coords; i++) { double diff = x1 [i] - x2 [i]; sum += diff * diff; } return sum; } //——————————————————————————————————————————————————————————————————————————————
C_AO_CFO 类的 Revision() 方法负责更新优化过程中找到的最佳解决方案。该方法遍历 popSize 群体中的所有个体(或样本)。对于每个代理,检查其 a[p].f 适应度函数是否大于当前最佳 fB 值。如果满足条件,则将 fB 的值更新为等于代理的适应度函数,然后如果代理的解是最佳的,则复制代理的 cB 坐标。因此,Revision 方法会找到并存储所有代理中的最佳解决方案,一旦发现代理改进了结果,就会更新全局参数。
//—————————————————————————————————————————————————————————————————————————————— //--- Update the best solution void C_AO_CFO::Revision () { for (int p = 0; p < popSize; p++) { if (a [p].f > fB) { fB = a [p].f; ArrayCopy (cB, a [p].c, 0, 0, WHOLE_ARRAY); } } } //——————————————————————————————————————————————————————————————————————————————
测试结果
原始的、严格确定性的算法在其基本版本中显示出以下结果:
CFO|Central Force Optimization|30.0|1.0|0.1|0.1|0.9|0.1|
=============================
5 Hilly's; Func runs:10000; result:0.34508431921321436
25 Hilly's;Func runs:10000; result:0.2826594689557952
500 Hilly's;Func runs:10000; result:0.25174636412054047
=============================
5 Forest's; Func runs:10000; result:0.26234538930351947
25 Forest's; Func runs:10000; result:0.1852230195779629
500 Forest's; Func runs:10000; result:0.15353213276989314
=============================
5 Megacity's; Func runs:10000; result:0.24923076923076923
25 Megacity's; Func runs:10000; result:0.1261538461538462
500 Megacity's;Func runs:10000; result:0.09492307692307768
=============================
All score:1.95090 (21.68%)
有了这样的结果,该算法就不能包含在我们的评级表中。我们需要改进。因此,正如我上面所说,这个字符串中添加了随机性元素。如果没有它,确定性搜索就缺乏多种解决方案。
// Add a small random offset directly to the position probe [p].c [c] += currentNoiseFactor * g * u.RNDfromCI (-1.0, 1.0);
加入一点随机性之后(在每个探测器的运动中加入少量随机偏差),该算法能够开始探索意想不到的方向。我们再进行一次测试。现在我们可以观察到完全不同的结果,这些结果已经记录在我们的评级表中。
CFO|Central Force Optimization|30.0|1.0|0.1|0.1|0.9|0.1|1.0|
=============================
5 Hilly's; Func runs:10000; result:0.6096110105488222
25 Hilly's;Func runs:10000; result:0.5495761567207647
500 Hilly's;Func runs:10000; result:0.27830861578120414
=============================
5 Forest's; Func runs:10000; result:0.6341793648294705
25 Forest's; Func runs:10000; result:0.4683296629644541
500 Forest's; Func runs:10000; result:0.22540930020804817
=============================
5 Megacity's; Func runs:10000; result:0.5723076923076923
25 Megacity's; Func runs:10000; result:0.2347692307692307
500 Megacity's;Func runs:10000; result:0.09586153846153929
=============================
All score:3.66835 (40.76%)
现在我们可以看一下算法运行的可视化过程。可以看出,该算法在中维函数上表现良好,但在低维和高维函数上效果不够好。

Hilly 测试函数上的 CFO

Forest 测试函数上的 CFO
Megacity 测试函数上的 CFO
根据测试结果,该算法是顶尖的群体优化算法之一,排名第 42 位。
| # | 算法 | 描述 | Hilly | Hilly 最终 | Forest | Forest 最终 | Megacity (discrete) | Megacity 最终 | 最终结果 | 最大值的百分比 | ||||||
| 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 | 跨邻里搜索(across neighbourhood search) | 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(animal migration optimization 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) 演进战略((P+O) evolution strategies) | 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(stochastic diffusion search 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(archery algorithm 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 | ACS | 人工协同搜索(artificial cooperative search) | 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 |
| 13 | 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 |
| 14 | 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 |
| 15 | ASO | 无政府社会优化(anarchy society optimization) | 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 |
| 16 | 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 |
| 17 | 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 |
| 18 | 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 |
| 19 | DE | 差分进化(differential evolution) | 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 |
| 20 | 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 |
| 21 | CRO | 化学反应优化(chemical reaction optimization) | 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 |
| 22 | 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 |
| 23 | BSA | 鸟群算法(bird swarm algorithm) | 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 |
| 24 | HS | 和声搜索(harmony search) | 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 |
| 25 | SSG | 树苗播种和生长(saplings sowing and growing) | 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 |
| 26 | BCOm | 细菌趋化性优化 M(bacterial chemotaxis optimization 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 |
| 27 | 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 |
| 28 | (PO)ES | (PO) 进化策略((PO) evolution strategies) | 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 |
| 29 | TSm | 禁忌搜索 M(tabu search M) | 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 |
| 30 | BSO | 头脑风暴优化(brain storm optimization) | 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 |
| 31 | WOAm | Wale 优化算法 M(wale optimization algorithm 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 |
| 32 | AEFA | 人工电场算法(artificial electric field algorithm) | 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 |
| 33 | 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 |
| 34 | ACOm | 蚁群优化M(ant colony optimization 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 |
| 35 | BFO-GA | 细菌觅食优化 - ga(bacterial foraging optimization - 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 |
| 36 | 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 |
| 37 | ABHA | 人工蜂巢算法(artificial bee hive algorithm) | 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 |
| 38 | ACMO | 大气云模型优化(atmospheric cloud model optimization) | 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 |
| 39 | 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 |
| 40 | 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 |
| 41 | ATAm | 人工部落算法 M | 0.71771 | 0.55304 | 0.25235 | 1.52310 | 0.82491 | 0.55904 | 0.20473 | 1.58867 | 0.44000 | 0.18615 | 0.09411 | 0.72026 | 3.832 | 42.58 |
| 42 | CFO | 中心引力优化 | 0.60961 | 0.54958 | 0.27831 | 1.43750 | 0.63418 | 0.46833 | 0.22541 | 1.32792 | 0.57231 | 0.23477 | 0.09586 | 0.90294 | 3.668 | 40.76 |
| 43 | ASHA | 人工喷淋算法(artificial showering algorithm) | 0.89686 | 0.40433 | 0.25617 | 1.55737 | 0.80360 | 0.35526 | 0.19160 | 1.35046 | 0.47692 | 0.18123 | 0.09774 | 0.75589 | 3.664 | 40.71 |
| 44 | ASBO | 适应性社会行为优化(adaptive social behavior optimization) | 0.76331 | 0.49253 | 0.32619 | 1.58202 | 0.79546 | 0.40035 | 0.26097 | 1.45677 | 0.26462 | 0.17169 | 0.18200 | 0.61831 | 3.657 | 40.63 |
| 45 | MEC | 思维进化计算(mind evolutionary computation) | 0.69533 | 0.53376 | 0.32661 | 1.55569 | 0.72464 | 0.33036 | 0.07198 | 1.12698 | 0.52500 | 0.22000 | 0.04198 | 0.78698 | 3.470 | 38.55 |
| 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 | |
总结
CFO 算法基于物体间引力的原理,是一种将物理定律应用于解决复杂优化问题的有趣尝试。经过对我们的标准功能集的广泛测试,CFO 的效率达到了理论最大值的 40% 以上,在排名前 45 的基于群体的优化算法中排名第 42 位。应该指出的是,即使是这个适度的结果,也只能通过修改原始算法,在其最初的确定性中引入随机成分来实现。
尽管绩效指标相对较低,但 CFO 有许多吸引人的特点。首先,它有一个非常清晰的物理解释,这使它变得直观。基本概念的简单性(代表潜在解决方案的探测器会被更好的解决方案所吸引,就像物质物体会被质量更大的物体所吸引一样)保证了算法运行的透明性和易于实现性。
然而,测试也揭示了 CFO 存在一些重大局限性,需要进行修订或改进。过度依赖探测器的初始分布是关键问题之一 —— 由于确定性运动机制,探测器的初始位置在很大程度上预先决定了最终的搜索结果。
单向吸引机制,其中只有最好的解决方案会影响最差的解决方案,而不是相反,尽管它有逻辑依据,但会显著限制算法探索搜索空间的能力。引入一种自适应机制,允许较差解决方案的一些影响,特别是在搜索的早期阶段,可以增强算法检测解决方案空间中有前景区域的能力。

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

图 3.算法测试结果直方图(范围从 0 到 100,越高越好,其中 100 是可能的最大理论结果,存档中有一个用于计算评级表的脚本)
CFO 的优缺点:
优点:
- 对中等维函数效果很好。
缺点:
- 对于低维和高维函数,效果都不够理想。
- 对离散函数的搜索能力较弱。
这篇文章附有一个归档,其中包含当前版本的算法代码。文章作者不对规范算法描述的绝对准确性负责。为提高搜索能力,已对其中多项功能进行了修改。文章中提出的结论和判断都是基于实验结果。
本文中用到的程序
| # | 名称 | 类型 | 描述 |
|---|---|---|---|
| 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_CFO.mq5 | 脚本 | CFO 测试台 |
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/17167
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
MQL5自动化交易策略(第十九部分):包络线趋势反弹剥头皮交易——交易执行与风险管理(下篇)
您应当知道的 MQL5 向导技术(第 60 部分):推理学习(Wasserstein-VAE),配合移动平均线和随机振荡器形态
MQL5自动化交易策略(第十八部分):基于包络线趋势反弹的剥头皮交易——核心架构与信号生成(1)