English Русский Español Deutsch 日本語 Português
preview
神经元集群优化算法2(NOA2)

神经元集群优化算法2(NOA2)

MetaTrader 5测试者 |
26 0
Andrey Dik
Andrey Dik

内容

  1. 概述
  2. 算法实现
  3. 测试结果


概述

多年来,随着对优化算法的深入研究,我常受两大灵感启发:生物集群的自组织特性,以及神经网络的自适应学习能力。这种神经控制水平逐渐影响“Boid”的行为,从早期迭代中主导探索的策略转变为在发现有前景区域时倾向于利用的移动方式。这两种模式的融合促使我开发出一种混合算法,该算法将克雷格·雷诺兹(Craig Reynolds)的“Boids”模型所体现的空间智能与神经网络的自适应学习能力相结合。

我的研究源于一个发现:传统集群算法虽擅长通过集体行为探索复杂空间,却往往缺乏从探索历程中学习的能力。反之,神经网络虽擅长学习复杂模式,但直接用于优化问题时,可能难以有效探索空间模式。引发我研究的问题看似简单:如果群体中的每个智能体都能利用专门的神经网络优化移动策略,结果会如何?

由此产生的算法采用遵循经典“Boid”规则(凝聚力、分离性和对齐性)的智能体,使它们能够自我组织并高效地探索搜索空间。然而,与传统“Boid”实现不同的是,每个智能体都配备了一个多层神经网络,该网络不断从智能体的经验中学习,根据适应度景观的具体特征调整其移动策略。这种神经控制机制会逐渐改变“Boid”的行为:从早期迭代以探索为主,转变为发现潜力区域后倾向于应用。

在开发过程中,最吸引我的是观察神经网络如何根据其在搜索空间中的位置演化出不同的策略。靠近有前景区域附近的智能体发展出加强局部利用的神经模式,而位于稀疏区域的智能体则支持探索行为。这种涌现出的特化现象源于个体学习,使群体具备了异质性和情境依赖性行为,而无需全局协调机制。

在本文中,我将介绍NOA2算法的架构、实现细节和性能分析,并展示其在各种基准函数上的能力。 


算法实现

正如我前面提及的,神经元集群算法的主要思想是融合两种模式:集群算法的集体智能和神经网络的自适应学习能力。

在克雷格·雷诺兹提出的传统“Boids”算法中,智能体遵循三条简单规则:聚合(向群体中心移动)、分离(避免碰撞)和对齐(与邻居保持相同速度)。这些规则创造了逼真的群体行为,类似于鸟群中鸟类的行为。神经元集群算法则扩展了这一概念,为每个智能体配备了一个个体神经网络,该网络从智能体探索搜索空间的经验中学习。此神经网络执行两个关键功能:

  1. 自适应运动控制根据智能体的当前状态和运动历史调整其速度。
  2. 修改标准“Boids”规则根据情境动态调整聚合、分离和对齐规则的影响力。

由此产生了一种混合算法,其中每个智能体保留有效探索空间所需的社会行为,同时通过学习单独适应适应度函数景观。这样创造了探索与利用之间的自我调节平衡。

该方法的核心优势在于,智能体能独立学习最优运动策略,使算法自动适应不同优化场景;同时,去中心化的集体行为保留了空间探索能力。举个简单的类比:想象一群鸟在空中飞翔。它们以协调的方式移动:没有碰撞,它们聚集在一起,朝同一方向飞行。此行为可以用三条简单的规则来描述:与邻居保持亲近(不要脱离群体)、不要与邻居相撞(保持距离)以及朝同一方向飞行(保持共同航向)。

这就是所谓的“Boid”(“鸟形”)算法的基础。鸟群中的每只鸟不仅遵循这些规则,还要从自身的经验中学习。鸟儿会记住哪些行为是成功的(如找到更多食物),哪些不是。久而久之,它们会变得更聪明,在飞行决策上也更加明智。这就是神经元集群算法的精髓:将群体运动的简单规则与每个参与者从自身经验中学习的能力相结合。名称中的“神经”表示学习的能力。这种方法特别有趣,因为它结合了集体探索的力量(没有人会错过重要区域)与个体学习的好处(每个人都试图在自己的研究领域成为佼佼者)。

NOA2图示

图例1. NOA2算法运行  

上图展示了以下关键元素:优化场景,其中黄紫色区域代表全局最优解,橙色和紫色区域代表局部最优解,等高线则展示了适应度函数场景的“高度”。 

不同群体的智能体:蓝色智能体探索全局最优解区域,紫色智能体聚集在第一个局部最优解周围,绿色智能体探索第二个局部最优解,红色智能体则对空间进行随机探索。箭头表示“Boids”智能体的移动方向。红点标记的是当前找到的最优解。

NOA2图表

图例2. NOA2算法运行  

该图表包含:初始化模块(红色)、“Boid”神经网络(粉色)、带有子模块的算法迭代(蓝色)、自适应机制(绿色)、神经网络架构可视化(含连接示例)。底部区域展示了神经网络结构的简易图表、“Boid”移动规则的可视化,以及探索与利用之间的平衡。

了解基本概念后,我们可以继续描述该算法的伪代码。

初始化:

  • 设定搜索参数并创建智能体种群。
  • 对于每个智能体:初始化随机位置、低速、神经网络(输入层→隐藏层→输出层)以及空的经验记忆。
  • 将全局最佳适应度设为负无穷,停滞计数器设置为0。

主循环:

  • 对于每次迭代:

    评估阶段:

    • 计算所有智能体的适应度。
    • 更新个体和全局最优位置。
    • 将实验数据保存在记忆缓冲区中。

    停滞管理:

    • 如果没有改进:增加探索力度,不时重启表现不佳的智能体。
    • 否则:逐渐降低探索力度。

    神经处理:

    • 对于每个智能体:
      • 前向传播:输入(位置、速度、与最优位置的距离、适应度)→ 隐藏层 → 输出。
      • 如果已经收集到足够经验且检测到改进:更新神经网络权重。

    移动更新:

    • 对于每个智能体:
      • 计算标准“Boid”作用力(聚合力、分离力、对齐力)。
      • 应用经神经网络修改的作用力并直接进行神经校正。
      • 以一定概率添加随机尝试分量。
      • 强制执行速度限制并遵守边界条件。
      • 根据最终速度更新位置。

辅助计算:

  • 质量计算:对适应度值进行标注化处理,为每个智能体分配质量。
  • 聚合力:向附近智能体的加权质心移动。
  • 分离力:避免与邻居过于拥挤。
  • 对齐力:与附近智能体协调速度。
  • 神经网络更新:基于适应度提升的简化反向传播。

返回:全局最优位置和适应度。

现在,一切准备就绪,可以编写算法代码。定义S_NeuroBoids_Agent结构体,该结构体将代表一个基于神经网络架构的神经元集群智能体,用于执行优化任务。在实现过程中,智能体具有以下关键组件和功能:

坐标和速度

  • x [] — 智能体的当前坐标。
  • dx [] — 智能体的当前速度。
神经网络
  • inputs [] — 神经元的输入值(坐标、速度、与已知最优位置的距离等)。
  • outputs [] — 神经网络的输出值(速度校正和适应参数)。
  • weights [] — 神经网络中连接权重。
  • biases [] — 神经元的偏置。
内存
  • memory [] — 用于存储先前位置及其适应度值的数组。
  • memory_size — 记忆存储的最大容量。
  • memory_index — 记忆中的当前索引。
适应度和权重
  • best_local_fitness — 智能体的最优局部适应度。
  • m — 智能体质量。
  • cB [] — 智能体找到的最优位置的坐标。
  • fB — 最优位置的适应度函数值。

初始化(Init) — 将所有数组和变量初始化为0或随机值。设置数组大小,将影响因子(权重和偏置)初始化为较小的随机值。

激活函数 — Tanh、ReLU、Sigmoid:神经网络中使用的各种激活函数。

更新输入数据(UpdateInputs)使用当前坐标、速度、与已知最优位置的距离以及当前适应度值填充输入数组。

前向传播使用激活函数,根据输入、权重和偏置计算神经网络的输出。

经验学习 (Learn)如果当前经验优于先前经验,则根据积累的经验更新权重和偏置。

记忆经验(MemorizeExperience)将智能体的当前坐标和适应度保存至内存中。

最优位置更新(UpdateBestPosition) — 如果当前适应度值优于先前找到的值,则更新最优位置。

//——————————————————————————————————————————————————————————————————————————————
// Neuro-boid agent structure
//——————————————————————————————————————————————————————————————————————————————
struct S_NeuroBoids_Agent
{
    double x       [];            // current coordinates
    double dx      [];            // current speeds
    double inputs  [];            // neuron inputs
    double outputs [];            // neuron outputs
    double weights [];            // neuron weights
    double biases  [];            // neuron biases
    double memory  [];            // memory of previous positions and their fitnesses
    int    memory_size;           // memory size for accumulating experience
    int    memory_index;          // current index in memory
    double best_local_fitness;    // best local agent fitness
    double m;                     // boid mass
    double cB [];                 // best position coordinates
    double fB;                    // fitness function value

    // Agent initialization
    void Init (int coords, int neuron_size)
    {
      ArrayResize (x, coords);
      ArrayResize (dx, coords);
      ArrayInitialize (x, 0.0);
      ArrayInitialize (dx, 0.0);

      // Initialize the best position structure
      ArrayResize     (cB, coords);
      ArrayInitialize (cB, 0.0);
      fB = -DBL_MAX;

      // Inputs: coordinates, speeds, distance to best, etc.
      int input_size = coords * 2 + 2; // x, dx, dist_to_best, current_fitness
      ArrayResize (inputs, input_size);
      ArrayInitialize (inputs, 0.0);

      // Outputs: Speed correction and adaptive factors for flock rules
      int output_size = coords + 3; // dx_correction + 3 adaptive parameters
      ArrayResize (outputs, output_size);
      ArrayInitialize (outputs, 0.0);

      // Weights and biases for each output
      ArrayResize (weights, input_size * output_size);
      ArrayInitialize (weights, 0.0);
      ArrayResize (biases, output_size);
      ArrayInitialize (biases, 0.0);

      // Initialize weights and biases with small random values
      for (int i = 0; i < ArraySize (weights); i++)
      {
        weights [i] = 0.01 * (MathRand () / 32767.0 * 2.0 - 1.0);
      }

      for (int i = 0; i < ArraySize (biases); i++)
      {
        biases [i] = 0.01 * (MathRand () / 32767.0 * 2.0 - 1.0);
      }

      // Initialize memory for accumulating experience
      memory_size = 10;
      ArrayResize (memory, memory_size * (coords + 1)); // coordinates + fitness
      ArrayInitialize (memory, 0.0);
      memory_index = 0;
      best_local_fitness = -DBL_MAX;

      // Initialize mass
      m = 0.5;
    }

    // Activation function - hyperbolic tangent
    double Tanh (double input_val)
    {
      return MathTanh (input_val);
    }

    // ReLU activation function
    double ReLU (double input_val)
    {
      return (input_val > 0.0) ? input_val : 0.0;
    }

    // Sigmoid activation function
    double Sigmoid (double input_val)
    {
      return 1.0 / (1.0 + MathExp (-input_val));
    }

    // Updating neural network inputs - Corrected version
    void UpdateInputs (double &global_best [], double current_fitness, int coords_count)
    {
      int input_index = 0;

      // Coordinates
      for (int c = 0; c < coords_count; c++)
      {
        inputs [input_index++] = x [c];
      }

      // Speeds
      for (int c = 0; c < coords_count; c++)
      {
        inputs [input_index++] = dx [c];
      }

      // Distance to the best global solution
      double dist_to_best = 0;
      for (int c = 0; c < coords_count; c++)
      {
        dist_to_best += MathPow (x [c] - global_best [c], 2);
      }
      dist_to_best = MathSqrt (dist_to_best);
      inputs [input_index++] = dist_to_best;

      // Current fitness function
      inputs [input_index++] = current_fitness;
    }

    // Direct distribution over the network
    void ForwardPass (int coords_count)
    {
      int input_size = ArraySize (inputs);
      int output_size = ArraySize (outputs);

      // For each output, calculate the weighted sum of the inputs + bias
      for (int o = 0; o < output_size; o++)
      {
        double sum = biases [o];

        for (int i = 0; i < input_size; i++)
        {
          sum += inputs [i] * weights [o * input_size + i];
        }

        // Apply different activation functions depending on the output
        if (o < coords_count) // Use the hyperbolic tangent to correct the speed
        {
          outputs [o] = Tanh (sum);
        }
        else // Use sigmoid for adaptive parameters
        {
          outputs [o] = Sigmoid (sum);
        }
      }
    }

    // Learning from accumulated experience
    void Learn (double learning_rate, int coords_count)
    {
      if (memory_index < memory_size) return; // Insufficient experience

      // Find the best experience in memory
      int best_index = 0;
      double best_fitness = memory [coords_count]; // The first fitness function in memory

      for (int i = 1; i < memory_size; i++)
      {
        double fitness = memory [i * (coords_count + 1) + coords_count];
        if (fitness > best_fitness)
        {
          best_fitness = fitness;
          best_index = i;
        }
      }

      // If the current experience is not better than the previous one, do not update the weights
      if (best_fitness <= best_local_fitness) return;

      best_local_fitness = best_fitness;

      // Simple method for updating weights
      int input_size = ArraySize (inputs);
      int output_size = ArraySize (outputs);

      // Simple form of gradient update
      for (int o = 0; o < output_size; o++)
      {
        for (int i = 0; i < input_size; i++)
        {
          int weight_index = o * input_size + i;
          if (weight_index < ArraySize (weights))
          {
            weights [weight_index] += learning_rate * outputs [o] * inputs [i];
          }
        }

        // Update offsets
        biases [o] += learning_rate * outputs [o];
      }
    }

    // Save current experience (coordinates and fitness)
    void MemorizeExperience (double fitness, int coords_count)
    {
      int offset = memory_index * (coords_count + 1);

      // Save the coordinates
      for (int c = 0; c < coords_count; c++)
      {
        memory [offset + c] = x [c];
      }

      // Save the fitness function
      memory [offset + coords_count] = fitness;

      // Update the memory index
      memory_index = (memory_index + 1) % memory_size;
    }

    // Update the agent's best position
    void UpdateBestPosition (double current_fitness, int coords_count)
    {
      if (current_fitness > fB)
      {
        fB = current_fitness;
        ArrayCopy (cB, x, 0, 0, WHOLE_ARRAY);
      }
    }
};

C_AO_NOA2类是NOA2算法的一种实现,它继承自C_AO基类。让我们深入了解一下该类的结构和关键要点。

主要参数
  • popSize — 智能体(“Boid”)种群规模。
  • cohesionWeight、cohesionDist — 聚合规则的权重和距离。
  • separationWeight、separationDist — 分离规则的权重和距离。
  • alignmentWeight、alignmentDist — 对齐规则的权重和距离。
  • maxSpeed和minSpeed — 智能体的最大和最小速度。
  • learningRate — 神经网络的学习率。
  • neuralInfluence — 神经网络对智能体移动的影响程度。
  • explorationRate — 在解空间中进行随机探索的概率。
  • m_stagnationCounter和m_prevBestFitness — 停滞计数器和先前的最优适应度值。
类方法
  • SetParams() — 根据“params”数组中存储的值设置算法参数的方法。
  • Init() — 初始化方法,接收参数以定义智能体的取值范围。该方法为算法的运行设置初始条件。
  • Moving() — 负责根据各种交互规则移动智能体。
  • Revision() — 用于在算法执行过程中修订智能体的状态。

智能体:S_NeuroBoids_Agent agent[] — 表示种群中“Boid”的智能体数组。

私有方法(仅在类内部使用):

  • CalculateMass() — 计算智能体的质量。
  • Cohesion() — 聚合规则。
  • Separation() — 分离规则。
  • Alignment() — 对齐规则。
  • LimitSpeed() — 限制智能体速度。
  • KeepWithinBounds() — 将智能体保持在给定边界内。
  • Distance() — 计算两个智能体之间的距离。
  • ApplyNeuralControl() — 对智能体应用神经网络控制。
  • AdaptiveExploration() — 实现自适应探索。
//——————————————————————————————————————————————————————————————————————————————
// Class of neuron-like optimization algorithm inherited from C_AO
//——————————————————————————————————————————————————————————————————————————————
class C_AO_NOA2 : public C_AO
{
  public: //--------------------------------------------------------------------
  ~C_AO_NOA2 () { }
  C_AO_NOA2 ()
  {
    ao_name = "NOA2";
    ao_desc = "Neuroboids Optimization Algorithm 2 (joo)";
    ao_link = "https://www.mql5.com/en/articles/17497";

    popSize          = 50;     // population size

    cohesionWeight   = 0.6;    // cohesion weight
    cohesionDist     = 0.001;  // cohesion distance

    separationWeight = 0.005;  // separation weight
    separationDist   = 0.03;   // separation distance

    alignmentWeight  = 0.1;    // alignment weight
    alignmentDist    = 0.1;    // alignment distance

    maxSpeed         = 0.001;  // maximum speed
    minSpeed         = 0.0001; // minimum speed 

    learningRate     = 0.01;   // neural network learning speed
    neuralInfluence  = 0.3;    // influence of the neural network on movement
    explorationRate  = 0.1;    // random exploration probability

    ArrayResize (params, 12);

    params [0].name  = "popSize";          params [0].val = popSize;

    params [1].name  = "cohesionWeight";   params [1].val  = cohesionWeight;
    params [2].name  = "cohesionDist";     params [2].val  = cohesionDist;

    params [3].name  = "separationWeight"; params [3].val  = separationWeight;
    params [4].name  = "separationDist";   params [4].val  = separationDist;

    params [5].name  = "alignmentWeight";  params [5].val  = alignmentWeight;
    params [6].name  = "alignmentDist";    params [6].val  = alignmentDist;

    params [7].name  = "maxSpeed";         params [7].val  = maxSpeed;
    params [8].name  = "minSpeed";         params [8].val  = minSpeed;

    params [9].name  = "learningRate";     params [9].val  = learningRate;
    params [10].name = "neuralInfluence";  params [10].val = neuralInfluence;
    params [11].name = "explorationRate";  params [11].val = explorationRate;

    // Initialize the stagnation counter and the previous best fitness value
    m_stagnationCounter = 0;
    m_prevBestFitness   = -DBL_MAX;
  }

  void SetParams ()
  {
    popSize          = (int)params [0].val;

    cohesionWeight   = params [1].val;
    cohesionDist     = params [2].val;

    separationWeight = params [3].val;
    separationDist   = params [4].val;

    alignmentWeight  = params [5].val;
    alignmentDist    = params [6].val;

    maxSpeed         = params [7].val;
    minSpeed         = params [8].val;

    learningRate     = params [9].val;
    neuralInfluence  = params [10].val;
    explorationRate  = params [11].val;
  }

  bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0);
  void Moving   ();
  void Revision ();

  //----------------------------------------------------------------------------
  double cohesionWeight;     // cohesion weight
  double cohesionDist;       // cohesion distance

  double separationWeight;   // separation weight
  double separationDist;     // separation distance

  double alignmentWeight;    // alignment weight
  double alignmentDist;      // alignment distance

  double minSpeed;           // minimum speed
  double maxSpeed;           // maximum speed

  double learningRate;       // neural network learning speed
  double neuralInfluence;    // influence of the neural network on movement
  double explorationRate;    // random exploration probability

  int m_stagnationCounter;   // stagnation counter
  double m_prevBestFitness;  // previous best fitness value

  S_NeuroBoids_Agent agent []; // agents (boids)

  private: //-------------------------------------------------------------------
  double distanceMax;     // maximum distance
  double speedMax [];     // maximum speeds by measurements

  int neuron_size;        // neuron size (number of inputs)

  void   CalculateMass       ();                                  // calculation of agent masses 
  void   Cohesion            (S_NeuroBoids_Agent &boid, int pos); // cohesion rule
  void   Separation          (S_NeuroBoids_Agent &boid, int pos); // separation rule
  void   Alignment           (S_NeuroBoids_Agent &boid, int pos); // alignment rule
  void   LimitSpeed          (S_NeuroBoids_Agent &boid);          // speed limit
  void   KeepWithinBounds    (S_NeuroBoids_Agent &boid);          // keep within bounds
  double Distance            (S_NeuroBoids_Agent &boid1, S_NeuroBoids_Agent &boid2); // calculate distance
  void   ApplyNeuralControl  (S_NeuroBoids_Agent &boid, int pos); // apply neural control
  void   AdaptiveExploration ();                                  // adaptive research
};
//——————————————————————————————————————————————————————————————————————————————

Init方法负责初始化算法和智能体的参数,其工作始于调用StandardInit方法,并向其传递取值范围的最小值和最大值数组以及搜索步长。如果标准初始化失败,该方法会立即返回“false”。接下来,设置将在智能体神经网络中使用的神经元规模。

在此情况下,神经元规模的计算方式为坐标数量乘以2,并额外添加两个值——dist_to_best(到最优解的距离)和current_fitness(当前适应度)。智能体数组的规模会根据指定的popSize(种群规模)进行相应调整。随后,针对每个智能体调用Init方法,以初始化其参数,包括神经网络。

计算最大距离和速度

  • 将distanceMax变量初始化为0。
  • 针对每个坐标,根据取值范围的最大值与最小值之差来计算最大速度值。
  • 最大距离的计算方式为最大速度平方和的平方根。

将停滞计数器(m_stagnationCounter)重置为0,并将存储先前最优适应度值的变量(m_prevBestFitness)设置为可能的最小值。该方法会设置各种全局变量,例如聚合权重、分离权重、对齐权重、最大和最小速度、学习率、神经网络影响以及探索概率。如果所有步骤均成功执行,该方法将返回“true”,以确认初始化成功。

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_NOA2::Init (const double &rangeMinP  [],  // minimum search range
                      const double &rangeMaxP  [],  // maximum search range
                      const double &rangeStepP [],  // search step
                      const int    epochsP)         // number of epochs
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  // Determine the size of a neuron
  neuron_size = coords * 2 + 2; // x, dx, dist_to_best, current_fitness

  // Initialize the agents
  ArrayResize (agent, popSize);
  for (int i = 0; i < popSize; i++)
  {
    agent [i].Init (coords, neuron_size);
  }

  distanceMax = 0;
  ArrayResize (speedMax, coords);

  for (int c = 0; c < coords; c++)
  {
    speedMax [c] = rangeMax [c] - rangeMin [c];
    distanceMax += MathPow (speedMax [c], 2);
  }

  distanceMax = MathSqrt (distanceMax);

  // Reset the stagnation counter and the previous best fitness value
  m_stagnationCounter = 0;
  m_prevBestFitness   = -DBL_MAX;

  GlobalVariableSet ("#reset", 1.0);

  GlobalVariableSet ("1cohesionWeight",   params [1].val);
  GlobalVariableSet ("2cohesionDist",     params [2].val);

  GlobalVariableSet ("3separationWeight", params [3].val);
  GlobalVariableSet ("4separationDist",   params [4].val);

  GlobalVariableSet ("5alignmentWeight",  params [5].val);
  GlobalVariableSet ("6alignmentDist",    params [6].val);

  GlobalVariableSet ("7maxSpeed",         params [7].val);
  GlobalVariableSet ("8minSpeed",         params [8].val);

  GlobalVariableSet ("9learningRate",     params [9].val);
  GlobalVariableSet ("10neuralInfluence", params [10].val);
  GlobalVariableSet ("11explorationRate", params [11].val);

  return true;
}
//——————————————————————————————————————————————————————————————————————————————

Moving方法负责根据接收到的数据以及智能体之间的交互,实现智能体在环境中的移动。首先,该方法检查全局变量#reset的值。如果其值等于1.0,则重置参数(将“revision”变量设为“false”,然后将其重置回0.0)。从全局变量中提取各种参数的值,如聚合权重、分离权重、对齐权重、最小和最大速度、学习率、神经网络影响以及探索比例。可以配置这些参数,以改变智能体的行为。

如果“revision”变量为“false”,则初始化智能体的x坐标位置和dx速度。对于每个坐标,应用给定范围内的随机值,并将速度设置为较小的随机值。每个坐标还根据智能体的当前位置存储其状态。调用AdaptiveExploration()方法进行自适应探索,该方法可根据停滞状态改变智能体的行为。

智能体移动的主循环(针对每个智能体):

  • 保存智能体的当前经验。
  • 更新智能体找到的最优位置。
  • 更新智能体神经网络的输入数据。
  • 通过神经网络直接传播数据。
  • 基于累积的经验对智能体进行训练。
质量计算:调用CalculateMass()方法,根据智能体的质量评估它们之间的交互。

    应用规则与移动:再次开始遍历智能体的循环,并对每个智能体应用“Boid”算法的标准规则:

    • 聚合:智能体努力靠近彼此。
    • 分离:智能体避免彼此靠近,以防止碰撞。
    • 对齐:智能体尝试与邻居朝同一方向移动。
    调用ApplyNeuralControl()方法进行基于神经网络的控制。使用LimitSpeed()方法限制智能体的速度,并使用KeepWithinBounds()方法确保智能体保持在给定区域的边界内。在每个坐标上更新智能体的位置,将当前速度加到当前位置和状态上。

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_NOA2::Moving ()
    {
      double reset = GlobalVariableGet ("#reset");
      if (reset == 1.0)
      {
        revision = false;
        GlobalVariableSet ("#reset", 0.0);
      }
    
      // Get parameters from global variables for interactive configuration
      cohesionWeight   = GlobalVariableGet ("1cohesionWeight");
      cohesionDist     = GlobalVariableGet ("2cohesionDist");
    
      separationWeight = GlobalVariableGet ("3separationWeight");
      separationDist   = GlobalVariableGet ("4separationDist");
    
      alignmentWeight  = GlobalVariableGet ("5alignmentWeight");
      alignmentDist    = GlobalVariableGet ("6alignmentDist");
    
      maxSpeed         = GlobalVariableGet ("7maxSpeed");
      minSpeed         = GlobalVariableGet ("8minSpeed");
    
      learningRate     = GlobalVariableGet ("9learningRate");
      neuralInfluence  = GlobalVariableGet ("10neuralInfluence");
      explorationRate  = GlobalVariableGet ("11explorationRate");
    
      // Initialization of initial positions and speeds
      if (!revision)
      {
        for (int i = 0; i < popSize; i++)
        {
          for (int c = 0; c < coords; c++)
          {
            agent [i].x [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
            agent [i].dx [c] = (rangeMax [c] - rangeMin [c]) * u.RNDfromCI (-1.0, 1.0) * 0.001;
    
            a [i].c [c] = u.SeInDiSp (agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
    
        revision = true;
        return;
      }
    
      // Adaptive research depending on stagnation
      AdaptiveExploration ();
    
      //----------------------------------------------------------------------------
      // Main loop of boid movement
      for (int i = 0; i < popSize; i++)
      {
        // Save the current experience
        agent [i].MemorizeExperience (a [i].f, coords);
    
        // Update the agent's best position
        agent [i].UpdateBestPosition (a [i].f, coords);
    
        // Update the neural network inputs
        agent [i].UpdateInputs (cB, a [i].f, coords);
    
        // Forward propagation through the neural network
        agent [i].ForwardPass (coords);
    
        // Learning from accumulated experience
        agent [i].Learn (learningRate, coords);
      }
    
      // Calculate masses
      CalculateMass ();
    
      // Application of rules and movement
      for (int i = 0; i < popSize; i++)
      {
        // Standard rules of the boid algorithm
        Cohesion (agent [i], i);
        Separation (agent [i], i);
        Alignment (agent [i], i);
    
        // Apply neural control
        ApplyNeuralControl (agent [i], i);
    
        // Speed limit and keeping within bounds
        LimitSpeed (agent [i]);
        KeepWithinBounds (agent [i]);
    
        // Update positions
        for (int c = 0; c < coords; c++)
        {
          agent [i].x [c] += agent [i].dx [c];
          a [i].c [c] = u.SeInDiSp (agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    CalculateMass方法根据智能体的适应度(生产率)计算其“质量”,并对这些值进行标准化处理。maxMass和minMass变量分别初始化为-DBL_MAX(负的最大的双精度浮点数)和DBL_MAX(最大的双精度浮点数)。这些变量将用于找出整个智能体种群中的最高和最低适应度值。该方法通过检查“popSize”(种群规模)是否大于0,来确认是否存在至少一个智能体。如果没有智能体,则终止该方法。在第一个循环中,遍历所有智能体(从0到popSize),并针对每个智能体检查其适应度(a[i].f的值)是否大于当前的maxMass或小于minMass。

    结束该循环后,将确定所有智能体中的最大和最小适应度值。在第二个循环中,再次遍历所有智能体,并针对每个智能体,使用u.Scale函数计算其“质量”(m变量),该函数对适应度值进行标准化处理。 

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_NOA2::CalculateMass ()
    {
      double maxMass = -DBL_MAX;
      double minMass = DBL_MAX;
    
      // Check for data presence before calculations
      if (popSize <= 0) return;
    
      for (int i = 0; i < popSize; i++)
      {
        if (a [i].f > maxMass) maxMass = a [i].f;
        if (a [i].f < minMass) minMass = a [i].f;
      }
    
      for (int i = 0; i < popSize; i++)
      {
        agent [i].m = u.Scale (a [i].f, minMass, maxMass, 0.1, 1.0);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    ApplyNeuralControl方法负责根据神经网络输出对“Boid”型智能体施加控制。该方法遍历智能体的所有坐标“coords”。对于每个坐标c,首先检查当前索引是否未超出boid.outputs输出数组的边界。如果索引有效,则根据神经网络输出中对应值乘以neuralInfluence(神经网络影响系数)来调整智能体的速度dx[c]。

    为简化后续检查,获取boid.outputs数组的大小。检查聚合因子cohesion_factor、分离因子separation_factor、对齐因子alignment_factor对应的索引是否在输出数组范围内。如果索引越界,则赋默认值0.5。这些比例用于缩放基础权重,以体现神经网络的影响。通过调整聚合、分离和对齐的权重来影响智能体的行为:

    • local_cohesion设置聚合权重。
    • local_separation设置分离权重。
    • local_alignment设置对齐权重。

    该方法通过给定的xplorationRate(探索概率)判断是否应执行随机探索。如果满足条件,则随机选择一个坐标c进行扰动。扰动大小perturbation_size根据智能体质量(适应度度量)和该坐标的可能取值范围计算。从而控制随机变化的幅度,使其与智能体质量无关。将范围在[-perturbation_size, perturbation_size]内的随机扰动值加到速度dx[c]上。ApplyNeuralControl方法将神经网络运算结果整合到智能体运动管理中。根据数据调整智能体的速度,同时兼顾随机探索的需求。

    //——————————————————————————————————————————————————————————————————————————————
    // Apply neural control to a boid
    void C_AO_NOA2::ApplyNeuralControl (S_NeuroBoids_Agent &boid, int pos)
    {
      // Use the neural network outputs to correct the speed
      for (int c = 0; c < coords; c++)
      {
        // Make sure the index is not outside the array bounds 
        if (c < ArraySize (boid.outputs))
        {
          // Apply neural speed correction with a given influence
          boid.dx [c] += boid.outputs [c] * neuralInfluence;
        }
      }
    
      // Use the neural network outputs to adapt the flock parameters
      // Check that the indices do not go beyond the array bounds
      int output_size = ArraySize (boid.outputs);
    
      double cohesion_factor   = (coords < output_size) ? boid.outputs [coords] : 0.5;
      double separation_factor = (coords + 1 < output_size) ? boid.outputs [coords + 1] : 0.5;
      double alignment_factor  = (coords + 2 < output_size) ? boid.outputs [coords + 2] : 0.5;
    
      // Scale the base weights considering neural adaptation
      // These variables are local and do not change global parameters
      double local_cohesion   = cohesionWeight * (0.5 + cohesion_factor);
      double local_separation = separationWeight * (0.5 + separation_factor);
      double local_alignment  = alignmentWeight * (0.5 + alignment_factor);
    
      // Random study with a given probability
      if (u.RNDprobab () < explorationRate)
      {
        // Select a random coordinate for perturbation
        int c = (int)u.RNDfromCI (0, coords - 1);
    
        // Adaptive perturbation size depending on mass (fitness)
        double perturbation_size = (1.0 - boid.m) * (rangeMax [c] - rangeMin [c]) * 0.01;
    
        // Add random perturbation
        boid.dx [c] += u.RNDfromCI (-perturbation_size, perturbation_size);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    
    

    C_AO_NOA2类中的AdaptiveExploration方法根据优化过程中的停滞阶段实现自适应探索机制。该方法首先检查目标函数值fB相较于先前最优值m_prevBestFitness是否发生变化。如果两者差值小于0.000001,则判定为无进展,停滞计数器m_stagnationCounter递增。否则,终止停滞状态,重置计数器,并将当前fB值存储为新的最优值。

    当停滞次数超过20次时,随机探索概率explorationRate将提升,但上限设为0.5以避免过度随机化。每经历50次停滞迭代,执行部分重启操作:种群中70%的智能体在rangeMin至rangeMax范围内重新随机初始化,与此同时,其余30%的顶尖智能体的当前位置保持不变。重启后,重置停滞计数器。

    如果检测到进展且参数数量params大于11,则从参数数组中设置探索概率"explorationRate"。如果参数数量不足,则设置为默认值0.1。

    //——————————————————————————————————————————————————————————————————————————————
    // Adaptive research depending on stagnation
    void C_AO_NOA2::AdaptiveExploration ()
    {
      // Determine if there is progress in the search
      if (MathAbs (fB - m_prevBestFitness) < 0.000001)
      {
        m_stagnationCounter++;
      }
      else
      {
        m_stagnationCounter = 0;
        m_prevBestFitness = fB;
      }
    
      // Increase research during stagnation
      if (m_stagnationCounter > 20)
      {
        // Increase the probability of random exploration
        explorationRate = MathMin (0.5, explorationRate * 1.5);
    
        // Perform a partial restart every 50 stagnation iterations
        if (m_stagnationCounter % 50 == 0)
        {
          // Restart 70% of the population leaving the best agents
          int restart_count = (int)(popSize * 0.7);
    
          for (int i = popSize - restart_count; i < popSize; i++)
          {
            for (int c = 0; c < coords; c++)
            {
              agent [i].x [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
              agent [i].dx [c] = (rangeMax [c] - rangeMin [c]) * u.RNDfromCI (-1.0, 1.0) * 0.001;
            }
          }
    
          // Reset the stagnation counter
          m_stagnationCounter = 0;
        }
      }
      else
      {
        // If progress is good enough, use the normal research level 
        if (11 < ArraySize (params))
        {
          explorationRate = params [11].val;
        }
        else
        {
          explorationRate = 0.1; // default value
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_NOA2类中的Cohesion方法负责实现“Boid”种群模型中智能体的“聚合”行为。变量声明:

    • centerX — 用于存储邻近智能体质心坐标的数组。其大小与智能体“coords”坐标数量一致。
    • numNeighbors — 邻域计数器,记录在给定距离(考虑质量)范围内的智能体数量。
    • sumMass — 邻近智能体质量总和,用于对质心坐标进行标准化处理。

    第一个循环(从0到popSize)遍历所有智能体,累加种群中除当前智能体(索引为pos)外的所有个体的质量。第二个循环遍历所有智能体,检查其与当前智能体的距离是否小于最大距离乘以聚合距离因子(cohesionDist)。如果满足条件,则将该智能体的坐标(按质量加权)累加至centerX,并递增numNeighbors计数器。

    完成邻域坐标与质量的累加后,检查是否存在邻域智能体(numNeighbors > 0)且质量总和有效(sumMass > 0.0),如果存在邻域,则通过除以质量总和对质心坐标进行标准化。接下来,将当前智能体的速度dx按cohesionWeight(聚合权重)向质心方向调整,使其向邻域质心移动(移动幅度受邻域质量影响)。

    Cohesion方法通过计算邻域质心并调整智能体速度,实现种群的“聚合”行为。这是模拟群体行为的核心机制之一。参数cohesionDist和cohesionWeight分别控制该行为的生效距离和质心对智能体运动的影响程度。

    //——————————————————————————————————————————————————————————————————————————————
    // Find the center of mass of other boids and adjust the speed towards the center of mass
    void C_AO_NOA2::Cohesion (S_NeuroBoids_Agent &boid, int pos)
    {
      double centerX [];
      ArrayResize (centerX, coords);
      ArrayInitialize (centerX, 0.0);
    
      int numNeighbors = 0;
      double sumMass = 0;
    
      for (int i = 0; i < popSize; i++)
      {
        if (pos != i) sumMass += agent [i].m;
      }
    
      for (int i = 0; i < popSize; i++)
      {
        if (pos != i)
        {
          if (Distance (boid, agent [i]) < distanceMax * cohesionDist)
          {
            for (int c = 0; c < coords; c++)
            {
              centerX [c] += agent [i].x [c] * agent [i].m;
            }
    
            numNeighbors++;
          }
        }
      }
    
      if (numNeighbors > 0 && sumMass > 0.0)
      {
        for (int c = 0; c < coords; c++)
        {
          centerX [c] /= sumMass;
          boid.dx [c] += (centerX [c] - boid.x [c]) * cohesionWeight;
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_NOA2类中的Separation方法实现了模型智能体的“分离”行为,旨在防止当前“Boid”与邻近智能体发生碰撞。moveX参数(与cohesionDist数组类似)用于存储推动“Boid”远离其他智能体的位移向量。

    其大小与智能体“coords”坐标数量一致。将moveX数组所有元素初始化为0,以避免随机值累积。随后遍历种群中所有智能体(索引从0到popSize)。检查每个智能体是否与当前“Boid”距离过近。通过Distance函数计算当前“Boid”与邻近智能体的距离。如果距离小于最大距离乘以分离距离因子(separationDist),则对每个坐标执行以下操作:用当前Boid”的坐标减去邻近智能体坐标,生成指向Boid”自身的向量(即推动其远离该智能体的方向)。

    完成所有智能体遍历后,将计算得到的moveX位移向量乘以separationWeight(分离权重),并累加到当前“Boid”的速度dx上。通过调整separationWeight可控制排斥力强度:值越大,“Boid”的避碰行为越显著。

    Separation方法使“Boid”与邻近个体保持安全距离,避免碰撞,这是模拟群体行为的关键机制之一。该行为有助于维持每个智能体的“个人空间”,促进更自然的群体交互。参数separationDist和separationWeight分别灵活控制排斥作用的生效半径和强度。

    //——————————————————————————————————————————————————————————————————————————————
    // Pushing away from other boids to avoid collisions
    void C_AO_NOA2::Separation (S_NeuroBoids_Agent &boid, int pos)
    {
      double moveX [];
      ArrayResize (moveX, coords);
      ArrayInitialize (moveX, 0.0);
    
      for (int i = 0; i < popSize; i++)
      {
        if (pos != i)
        {
          if (Distance (boid, agent [i]) < distanceMax * separationDist)
          {
            for (int c = 0; c < coords; c++)
            {
              moveX [c] += boid.x [c] - agent [i].x [c];
            }
          }
        }
      }
    
      for (int c = 0; c < coords; c++)
      {
        boid.dx [c] += moveX [c] * separationWeight;
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_NOA2类中的Alignment方法实现了模型中智能体的“对齐”行为,该行为使得“Boid”能够与邻近个体协调速度,形成更和谐统一的群体运动。avgDX数组用于存储邻近智能体平均速度向量。其大小与智能体“coords”坐标数量一致。numNeighbors计数器记录在给定距离范围内的邻域智能体的数量。

    循环遍历种群中的所有智能体。首先检查当前智能体是否为“Boid”自身(需跳过自身计算)。如果另一智能体与“Boid”的距离小于最大距离乘以alignmentDist(对齐距离因子),则将该智能体的当前速度dx累加至avgDX。并且递增numNeighbors计数器。循环结束后,如果存在邻域智能体(numNeighbors > 0),则通过将累加的速度向量除以邻域数量,计算得到平均速度(avgDX)。接下来,根据alignmentWeight(对齐权重)的比率,将“Boid”的当前速度dx调整为其邻域的平均速度。 

    Alignment方法使得“Boid”能够根据邻域个体的速度动态调整自身运动方向。这样可以促进群体运动的协调性,减少冲突或方向突变。参数alignmentDist和alignmentWeight分别控制对齐行为的生效范围和邻域平均速度对“Boid”速度的影响程度。 
    //——————————————————————————————————————————————————————————————————————————————
    // Align speed with other boids
    void C_AO_NOA2::Alignment (S_NeuroBoids_Agent &boid, int pos)
    {
      double avgDX [];
      ArrayResize (avgDX, coords);
      ArrayInitialize (avgDX, 0.0);
    
      int numNeighbors = 0;
    
      for (int i = 0; i < popSize; i++)
      {
        if (pos != i)
        {
          if (Distance (boid, agent [i]) < distanceMax * alignmentDist)
          {
            for (int c = 0; c < coords; c++)
            {
              avgDX [c] += agent [i].dx [c];
            }
    
            numNeighbors++;
          }
        }
      }
    
      if (numNeighbors > 0)
      {
        for (int c = 0; c < coords; c++)
        {
          avgDX [c] /= numNeighbors;
          boid.dx [c] += (avgDX [c] - boid.dx [c]) * alignmentWeight;
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_NOA2类中的LimitSpeed方法用于控制模型中智能体(“Boid”)的速度,确保其速度维持在合理范围内,避免过快或过慢的运动。"speed"作为存储“Boid”当前速度的变量,通过计算速度向量的长度得到。遍历所有坐标(coords),计算各坐标速度的平方(boid.dx[c] * boid.dx[c]),并将它们累加求和。从而得到速度向量长度的平方。使用MathSqrt函数计算平方和的平方根,得到速度向量的实际长度(即当前速度值)如果速度大于一个较小值(1e-10),程序将继续执行,如果当前速度小于允许的最小速度(speedMax[0] * minSpeed),则将速度向量标准化(除以当前长度),并增加至允许的最小速度值。

    如果当前速度超过允许的最大速度(speedMax[0] * maxSpeed),同样标准化速度向量并限制为最大速度值,如果速度为0或非常接近0,则使用u.RNDfromCI(-1.0, 1.0)函数乘以最大速度,为速度向量的每个坐标生成一个较小的随机值。

    LimitSpeed方法通过强制约束速度范围,防止“Boid”运动过慢或过快。从而提升了智能体模拟的真实性,因为它不允许速度发生显著变化,从而避免产生不自然的运动。参数minSpeed和maxSpeed可灵活配置,以适应不同模拟场景对智能体运动特性的需求。

    //——————————————————————————————————————————————————————————————————————————————
    // Speed limit
    void C_AO_NOA2::LimitSpeed (S_NeuroBoids_Agent &boid)
    {
      double speed = 0;
    
      for (int c = 0; c < coords; c++)
      {
        speed += boid.dx [c] * boid.dx [c];
      }
    
      speed = MathSqrt (speed);
    
      // If the speed is not zero (prevent division by zero)
      if (speed > 1e-10)
      {
        // If the speed is too low, increase it
        if (speed < speedMax [0] * minSpeed)
        {
          for (int c = 0; c < coords; c++)
          {
            boid.dx [c] = boid.dx [c] / speed * speedMax [c] * minSpeed;
          }
        }
        // If the speed is too high, reduce it
        else
          if (speed > speedMax [0] * maxSpeed)
          {
            for (int c = 0; c < coords; c++)
            {
              boid.dx [c] = boid.dx [c] / speed * speedMax [c] * maxSpeed;
            }
          }
      }
      else
      {
        // If the speed is almost zero, set a small random speed
        for (int c = 0; c < coords; c++)
        {
          boid.dx [c] = u.RNDfromCI (-1.0, 1.0) * speedMax [c] * minSpeed;
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_NOA2类中的KeepWithinBounds方法用于确保智能体(“Boid”)始终在指定的边界范围内运动。当“Boid”接近区域边缘时,该方法会改变其运动方向并施加一个反向推力,使其返回边界内。通过遍历所有坐标,支持在多维空间中实现边界约束。

    对于每个坐标,检查“Boid”位置(boid.x[c])是否低于最小边界(rangeMin[c]),如果是,则通过"boid.dx[c] *= -1.0"反转速度方向(boid.dx[c])。这就意味着“Boid”将向相反的方向移动。接下来,额外添加一个来自边界的微小推力(rangeMax[c] - rangeMin[c]) * 0.001,这样有助于将“Boid”推回区域内。

    对最大边界(rangeMax[c])进行类似的检查:如果“Boid”的位置超出最大值,其速度同样被反转并调整,但减去一个与前述情况相似的值。

    KeepWithinBounds 方法有效地将 boid 的运动限制在给定区域内,防止它们飞出边界,并确保它们返回区域内。 

    //——————————————————————————————————————————————————————————————————————————————
    // Keep the boid within the boundaries. If it gets too close to the edge,
    // push it back and change direction.
    void C_AO_NOA2::KeepWithinBounds (S_NeuroBoids_Agent &boid)
    {
      for (int c = 0; c < coords; c++)
      {
        if (boid.x [c] < rangeMin [c])
        {
          boid.dx [c] *= -1.0;
          // Add a small push from the border
          boid.dx [c] += (rangeMax [c] - rangeMin [c]) * 0.001;
        }
        if (boid.x [c] > rangeMax [c])
        {
          boid.dx [c] *= -1.0;
          // Add a small push from the border
          boid.dx [c] -= (rangeMax [c] - rangeMin [c]) * 0.001;
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_NOA2类的Distance方法用于计算多维空间中两个智能体(“Boid”)之间的距离。其使用公式计算欧几里得距离,”dist“是一个变量,用于存储两个“Boid”坐标之间差值的平方和。

    该方法启动一个遍历所有坐标的循环,从而可以在任意维度的空间中计算距离。对于每个c坐标,计算两个“Boid”对应坐标之间差值的平方:boid1.x[c] - boid2.x[c]。

    结果((boid1.x[c] - boid2.x[c])^2)被累加到”dist“变量中。循环结束后,”dist“变量将包含坐标差值的平方和。为获得实际距离,该方法使用MathSqrt计算平方和的平方根,这对应于欧几里得距离公式。

    //——————————————————————————————————————————————————————————————————————————————
    // Calculate the distance between two boids
    double C_AO_NOA2::Distance (S_NeuroBoids_Agent &boid1, S_NeuroBoids_Agent &boid2)
    {
      double dist = 0;
    
      for (int c = 0; c < coords; c++)
      {
        dist += MathPow (boid1.x [c] - boid2.x [c], 2);
      }
    
      return MathSqrt (dist);
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_NOA2类的Revision方法负责更新优化过程中找到的最优解信息。该过程涉及更新适应度函数值以及对应于该值的坐标。此方法还监控进展,并在有显著改进时调整算法参数。其遍历整个种群,由数组a表示,其中包含popSize(智能体数量)。

    在循环内部,检查每个智能体的适应度值(a[i].f)是否大于当前最优值(fB)。如果当前智能体显示出最优适应度值,则更新适应度函数的全局最优值,将fB赋值为新的a[i].f,之后更新最优解的坐标。对于每个c坐标,用当前智能体的值更新cB数组(包含最优解的坐标)。由于发现解决方案有所改进,停滞计数器(m_stagnationCounter)被重置为0,

    该方法使用hasProgress变量来确定是否取得了进展。这是通过计算适应度函数当前最优值与先前最优值之间的绝对差值(MathAbs(fB - m_prevBestFitness))来实现的,如果该差值大于0.000001,则认为进展明显。如果有进展,m_prevBestFitness将更新为当前最优fB值。
    探索速率explorationRate也会进行自适应调整:如果发现改进,则会根据params数组中的某些参数和explorationRate的当前值降低该速度。

    //——————————————————————————————————————————————————————————————————————————————
    // Update the best solution found
    void C_AO_NOA2::Revision ()
    {
      // Update the best coordinates and fitness function value
      for (int i = 0; i < popSize; i++)
      {
        // Update the global best solution
        if (a [i].f > fB)
        {
          fB = a [i].f;
          for (int c = 0; c < coords; c++)
          {
            cB [c] = a [i].c [c];
          }
    
          // Reset the stagnation counter when a better solution is found
          m_stagnationCounter = 0;
        }
      }
    
      // Check for progress to adapt the algorithm parameters
      bool hasProgress = MathAbs (fB - m_prevBestFitness) > 0.000001;
      if (hasProgress)
      {
        m_prevBestFitness = fB;
        explorationRate = MathMax (params [11].val * 0.5, explorationRate * 0.9);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    


    测试结果

    测试结果相当糟糕。

    NOA2|Neuroboids Optimization Algorithm 2 (joo)|50.0|0.6|0.001|0.005|0.03|0.1|0.1|0.1|0.01|0.01|0.3|0.1|
    =============================
    5 Hilly's; Func runs: 10000; result: 0.47680799582735267
    25 Hilly's; Func runs: 10000; result: 0.30763714006051013
    500 Hilly's; Func runs: 10000; result: 0.2544737238936433
    =============================
    5 Forest's; Func runs: 10000; result: 0.3238017030688524
    25 Forest's; Func runs: 10000; result: 0.20976876473929068
    500 Forest's; Func runs: 10000; result: 0.15740101965732595
    =============================
    5 Megacity's; Func runs: 10000; result: 0.27076923076923076
    25 Megacity's; Func runs: 10000; result: 0.14676923076923082
    500 Megacity's; Func runs: 10000; result: 0.09729230769230844
    =============================
    总分: 2.24472(24.94%)

    可视化效果显示算法搜索能力一般。通过全局变量调整算法的外部参数,用户能够灵活实验不同参数组合下“Boid”的集群行为,从而揭示出多种有趣的行为模式。以下呈现了行为模式中具有代表性的部分可视化示例。

    Hilly值

    NOA2在Hilly测试函数上

    Forest值

    NOA2在Forest测试函数上

    Megacity值

    NOA2在Megacity测试函数上

    根据测试结果,基础版NOA2算法已纳入我们的种群优化算法排名表,仅供信息参考。

    # AO 描述 Hilly值 Hilly最终值 Forest值 Forest最终值 Megacity (离散) 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 跨邻域搜索 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 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
    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 无序社会优化 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 差分进化 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 化学反应优化 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 鸟群算法 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 和声搜索 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 树苗播种和生长 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 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) 进化策略 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 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 头脑风暴优化 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 鲸鱼优化算法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 人工电场算法 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 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 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 人工蜂巢算法 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 大气云模型优化 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 人工淋浴算法 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 适应性社会行为优化 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 思维进化计算 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
    NOA2 神经元集群优化算法2(joo 0.47681 0.30764 0.25447 1.03892 0.32380 0.20977 0.15740 0.69097 0.27077 0.14677 0.09729 0.51483 2.245 24.94


    总结

    神经元集群优化算法(NOA2)是一种混合方法,融合了集群智能(Boid算法)与神经控制原理。其核心思想是通过神经网络自适应调控“Boid”智能体的行为参数。当前实现采用单层神经网络(无隐藏层)。其包含输入层(接收当前坐标、速度、与最优解的距离、适应度函数值)和输出层(定义速度修正值及集群行为规则的自适应参数)。

    此版本无中间层。定义输入数量为 (coords * 2 + 2),其中"coords"是搜索空间的维度数。输出包括每个坐标的修正值,以及用于调整集群规则的三个额外的参数。该算法具有大量可配置参数,这使得寻找最优参数组合变得困难。我尝试了各种组合,但仍未找到能在测试函数上展现最佳效果的理想组合。

    就其当前形式而言,该算法显然只能作为概念验证,总体上是混合优化方法领域中一个有趣的实验,但并未展现出足够出色的性能表现。其在测试中得分极低,只能被视为算法混合化方法的示例说明。对于感兴趣的研究人员和开发者们,NOA2代码可以作为起点,用于尝试不同的配置和参数,以及创建更先进的混合优化算法,充分利用基于种群的方法和机器学习方法的优势。

    标签

    图例3. 算法在相应测试中的颜色渐变表示

    图表

    图例4. 算法测试结果的直方图(评分范围为0到100,越高越好,其中100为理论上的最高可能得分,档案中附有计算排名表的脚本)

    NOA2的优缺点:

    优点:

    1. 这是个有趣的理念。

    缺点:

    1. 结果不佳。

    文章附有一个包含当前版本算法代码的归档文件。本文作者对标准算法描述的绝对准确性不承担责任。为提升搜索能力,已经对其中的许多算法进行了修改。文章中表述的结论和论断都是基于实验的结果。


    文中所用的程序

    # 名称 类型 描述
    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_NOA2.mq5
    脚本 NOA2测试台

    本文由MetaQuotes Ltd译自俄文
    原文地址: https://www.mql5.com/ru/articles/17497

    附加的文件 |
    NOA2.zip (186.45 KB)
    交易策略 交易策略
    各种交易策略的分类都是任意的,下面这种分类强调从交易的基本概念上分类。
    市场模拟:(第 11 部分):套接字(五) 市场模拟:(第 11 部分):套接字(五)
    我们开始实现 Excel 和 MetaTrader 5 之间的连接,但首先我们需要了解一些关键点。这样,你就不必绞尽脑汁去弄清楚为什么有些东西有效或无效。在您对集成 Python 和 Excel 的前景感到沮丧之前,让我们看看如何(在某种程度上)使用 xlwings 通过 Excel 控制 MetaTrader 5。我们在这里展示的内容将主要集中在教育目标上。但是,不要以为我们只能做这里涵盖的事情。
    新手在交易中的10个基本错误 新手在交易中的10个基本错误
    新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
    数据科学和机器学习(第 38 部分):外汇市场中的 AI 迁移学习 数据科学和机器学习(第 38 部分):外汇市场中的 AI 迁移学习
    从 ChatGPT 到自动驾驶汽车,这些占据头条的 AI 突破并非基于孤立模型,而是从各种模型或共同领域积累的知识转化而成。现在,同样“学一次,随处应用”的方式也可帮助我们在算法交易中变换人工智能模型。在本文中,我们会将探讨如何利用从各种工具获取的信息,帮助提升迁移学习的预测效果。