种群优化算法:人工蜂群

文章介绍了人工蜂群算法,这一灵感来源于蜜蜂群居行为的优化策略。通过模拟蜜蜂的舞蹈通信和群体行为,算法在多维空间中寻找全局最大值或最小值,避免陷入局部极值。文章详细阐述了算法的工作原理,包括蜜蜂的侦察和工蜂行为,以及区域排名和信息传递过程。
摘要由CSDN通过智能技术生成

群居昆虫是高度进化的生物,可以执行许多单体昆虫无法完成的复杂任务。 沟通、筑造复杂的巢穴、环境控制、保护、和分工只是蜜蜂已进化到在社会性群居地茁壮成长的少数行为。 蜂群属于群体生物,在寻找最佳解决方案方面表现出非凡的能力。 在自然界中,它们在蜂巢附近寻找花簇来采集花蜜和花粉。 有时搜索半径可以增加到几公里。 返回的蜜蜂则是通过即兴舞蹈报告它们的发现。

乍一看,这似乎是不可能的,但它们能够通过有节奏的运动相互传递有关地理位置的信息。 取决于它们同胞间的舞蹈强度,蜂群得到有关指定地点的花朵数量和估算花蜜量的结论。 潜在的食物越丰富,舞蹈动作就越激烈。 这种不寻常的现象是在 20 世纪中叶由昆虫研究员卡尔·冯·弗里施(Karl von Frisch)发现的。

多年来,蜜蜂觅食方法仅在生物学家之间研究。 然而,在开发新的优化算法时,应用群体行为的兴趣正在增长。 在 2005 年,Dervis Karaboga 教授对研究结果产生了兴趣。 他发表了一篇科学论文,是第一个描述群体智能模型的人,主要受到蜂群舞蹈的启发。 该模型被称为人工蜂群。

2. 算法说明

人工蜂群有许多不同的实现,区别在于蜂巢中管理蜂群的原则、以及区域探索规则上有所不同。 在本文中,我将讨论我对经典算法版本的解释。

该算法的思路是基于蜂群在寻找尽可能多的获取花蜜的地方时的行为。 首先,所有的蜜蜂都朝随机的方向飞出蜂巢,充当侦察员,试图寻找有花蜜的区域。 之后,蜜蜂返回蜂巢,并以特殊的方式告诉其它个体在哪里找到了花蜜、以及发现了多少花蜜。

工蜂被发配到发现的区域。 在这个地区发现的花蜜越多,向那个方向飞的蜜蜂就越多。 侦察兵再次飞去寻找其它区域,但均在已发现区域的附近。 因此,所有的蜜蜂被分为两种:采集花蜜的工蜂,和探索新区域的侦察蜂。 花蜜采集区域具有的量值,与其花蜜量相对应。 沿穿过区域中心的线,较低等级的区域由相对于较高等级的区域进行置换。

由图示,工蜂按区域分布可以在图例 1 中可视化。

图例 1. 取决于区域排名,前往该区域的蜜蜂数量

区域 1 的等级较高,它包含的花蜜最多,因此更多的蜜蜂倾向于飞往那里。 通过蜜蜂的数量,我们可以直观地判定区域 4 的等级(值)最低。 蜜蜂以特殊舞蹈的形式报告有关每个区域价值的信息。 每个蜂巢都有自己的舞姿,其中对该地区花蜜的方向和数量进行编程。

假设全局位置的极值是花蜜最多的区域,并且只有一个这样的区域。 其它地方也有花蜜,尽管数量较少。 蜜蜂生活在多维空间中,其中每个坐标代表函数的一个参数,且其需要优化。 如果我们正在寻找全局最大值,则发现的花蜜量就是此刻目标函数的值。 如果我们正在寻找全局最小值,那么将目标函数乘以 -1 就足够了。

由于花蜜采集区域具有确定的价值,因此只有等级最高的区域才有权移到(中心移位)花蜜浓度最高的地点。 较低等级的区域则移至最高浓度的中心,并检查与其它较高等级区域的交叉点。 以这样的方式,不允许蜜蜂集中在一些狭窄的小区域内,并且由工蜂提供的搜索空间服务应尽可能地高效,从而防止食物来源的枯竭。 在优化方面,这消除或最大限度地减少了陷入局部极值的情况。 在区域分散,并沿着等级链相互移动到其最优位置后,蜜蜂侦察兵将深入侦察它们的邻域。

许多养蜂人建议将侦察蜂遣派到搜索空间的随机区域,但我的经验表明,这种“侦察”的实际价值接近于零,并且只对一、两个维度有用。 换言之,如果我们谈论的是多维空间,那么自由度就会呈几何级数增加,并且很难偶然发现更有价值的花蜜来源。 蜂巢的资源只会被浪费。 将侦察兵派往已知搜索区域的邻域更有用,这样坐标就不会分散,而是专门集中在可能的花蜜来源区域。

如果这些区域彼此相交,则必须偏转中心,令这些区域仅接触边界。 如图例 2 所示。

图例 2. 等级较低的区域应偏转

区域的排位并非严格设定的,而是动态形成的。 侦察蜂的发现会被分配到它们飞过区域的附近地区。 如果发现更有价值的食物来源,该区域的中心将转移到那里。 它甚至可能成为新的最佳花蜜采集中心。 其余区域现在将相对于它偏转。 换言之,它们沿排位链并相对于它偏移。

传递信息的方法,称为蜂之舞,是整个蜂巢战略的基本要素。 蜂巢的可用资源理应合理地分配到采集区域,那么遣派的蜜蜂数量应与区域的价值成正比。 这意味着相同数量的蜜蜂将被遣派到同等价值的区域。

我们继续讨论算法本身。 下面列出了执行步骤:

  1. 所有蜜蜂都作为侦察员沿着搜索空间随机飞行。
  2. 测量来自每只蜜蜂采集的花蜜量。
  3. 分拣蜜蜂。
  4. 根据从蜜蜂获得的花蜜量信息分配区域。
  5. 将工蜂遣派到该区域。 该区域的花蜜越多,遣派的蜜蜂就越多。
  6. 在随机选择的区域附近派遣侦察蜂。
  7. 测量来自每只蜜蜂那里采集到的花蜜量。
  8. 按花蜜量对区域进行排名。
  9. 重复上述步骤 直到满足停止准则。

为了便于直观感知,我们制作了算法的框图,如图例 3。

图例 3. 算法框图

我们更详细地讲述蜂群算法代码。

一只蜜蜂是算法的基本逻辑单元。 它可以通过结构来讲述。 您可能还记得,蜜蜂的位置是由坐标、与采集的花蜜区域的隶属关系、以及花蜜的数量来设置的。 那么,蜂巢的蜜蜂可由一个数组表示。 每个都可以据其索引来访问。

//——————————————————————————————————————————————————————————————————————————————
struct S_Bee
{
  double c []; //coordinates
  int    aInd; //area index
  double n;    //nectar
};
//——————————————————————————————————————————————————————————————————————————————

第二个更大的逻辑单元是花蜜采集区域。 它由花蜜最集中的中心点,以及决定该区域价值的花蜜量来定义。 在最高等级的区域(列表中的第一个),中心坐标和花蜜的最高浓度重合。 而对于列表中排名第二和更低的区域,它们可能不一致,因为它们被转移了。 该区域的初始化以重置花蜜量指标,并将坐标分配给优化函数的相应编号的参数。

//——————————————————————————————————————————————————————————————————————————————
struct S_Area
{
  void Init (int coordinatesNumber)
  {
    n = -DBL_MAX;

    ArrayResize (cC, coordinatesNumber);
    ArrayResize (cB, coordinatesNumber);
  }

  double cC   []; //center coordinates
  double cB   []; //best coordinates
  double n;       //nectarAmount
};
//——————————————————————————————————————————————————————————————————————————————

我们将蜜蜂的舞蹈描述为一个结构,其数组将与区域的数量相对应。

//——————————————————————————————————————————————————————————————————————————————
struct S_BeeDance
{
  double start;
  double end;
};
//——————————————————————————————————————————————————————————————————————————————

我将蜂巢描述为一个类,设置搜索区域、蜜蜂、最佳坐标、以及在所有迭代中发现的最大花蜜量。 此外,针对蜜蜂和区域进行分类,以及移动蜜蜂和彼此相对区域的所有必要方法均在此处定义。 在此,我们可以看到以前算法中已经熟悉的函数声明:生成随机均匀分布的数字,缩放到一个范围,然后从范围内选择一个数字并带有步长。

//——————————————————————————————————————————————————————————————————————————————
class C_AO_ABC //Bees Hive
{
  //============================================================================
  public: S_Area areas     []; //nectar collection areas
  public: double rangeMax  []; //maximum search range
  public: double rangeMin  []; //manimum search range
  public: double rangeStep []; //step search
  public: S_Bee  bees      []; //all the bees of the hive
  public: double cB        []; //best coordinates
  public: double nB;           //nectar of the best coordinates

  public: void InitHive (const int    coordinatesP,      //number of opt. parameters
                         const int    beesNumberP,       //bees number
                         const int    workerBeesNumberP, //worker bees number
                         const int    areasNumberP,      //areas number
                         const double collectionRadiusP, //collection radius
                         const double scoutAreaRadiusP); //scout area radius

  public: void TasksForBees ();
  public: void CollectingNectar ();

  //============================================================================
  private: void   BeeFlight      (double &cC [] , S_Bee &bee);
  private: void   ScoutBeeFlight (double &cC [] , S_Bee &bee);
  private: void   MarkUpAreas    ();
  private: void   SortingBees    ();
  private: void   SortingAreas   ();
  private: double SeInDiSp       (double In, double InMin, double InMax, double Step);
  private: double RNDfromCI      (double Min, double Max);
  private: double Scale          (double In, double InMIN, double InMAX, double OutMIN, double OutMAX,  bool Revers);

  private: int    coordinates;      //coordinates number
  private: int    beesNumber;       //the number of all bees
  private: int    workerBeesNumber; //worker bees number
  private: int    areasNumber;      //areas number
  private: S_Bee  beesT       [];   //temporary, for sorting
  private: S_Area areasT      [];   //temporary, for sorting
  private: int    indA        [];   //array for indexes when sorting
  private: double valA        [];   //array for nectar values when sorting
  private: int    indB        [];   //array for indexes when sorting
  private: double valB        [];   //array for nectar values when sorting
  private: double areasRadius [];   //radius for each coordinate
  private: double scoutRadius [];   //scout radius for each coordinate

  private: double collectionRadius;   //collection radius
  private: double scoutAreaRadius;    //scout area radius
  private: double hypersphereRadius;  //hypersphere radius
  private: double distHyperspCenters; //distance hyperspheres centers
  private: double scoutHyperspRadius; //scout hypersphere radius
  private: bool   scouting;           //scouting flag

  private: S_BeeDance beeDance [];    //bee dance
};
//——————————————————————————————————————————————————————————————————————————————

每次新优化都必须从类初始化开始。 整个蜂巢和蜜蜂单体,以及区域的花蜜量值均被重置。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ABC::InitHive (const int    coordinatesP,      //number of opt. parameters
                         const int    beesNumberP,       //bees number
                         const int    workerBeesNumberP, //worker bees number
                         const int    areasNumberP,      //areas number
                         const double collectionRadiusP, //collection radius
                         const double scoutAreaRadiusP)  //scout area radius
{
  MathSrand (GetTickCount ());
  scouting = false;
  nB       = -DBL_MAX;

  coordinates      = coordinatesP;
  beesNumber       = beesNumberP;
  workerBeesNumber = workerBeesNumberP;
  areasNumber      = areasNumberP < 3 ? 3 : areasNumberP;

  collectionRadius = collectionRadiusP; //collection radius
  scoutAreaRadius  = scoutAreaRadiusP;  //scout area radius

  ArrayResize (areas,  areasNumber);
  ArrayResize (areasT, areasNumber);
  for (int i = 0; i < areasNumber; i++)
  {
    areas  [i].Init (coordinates);
    areasT [i].Init (coordinates);
  }

  ArrayResize (beeDance, areasNumber);

  ArrayResize (rangeMax,  coordinates);
  ArrayResize (rangeMin,  coordinates);
  ArrayResize (rangeStep, coordinates);
  ArrayResize (cB,        coordinates);

  ArrayResize (indA, areasNumber);
  ArrayResize (valA, areasNumber);

  ArrayResize (areasRadius, coordinates);
  ArrayResize (scoutRadius, coordinates);
  for (int i = 0; i < coordinates; i++)
  {
    areasRadius [i] = (rangeMax [i] - rangeMin [i]) * collectionRadius;
    scoutRadius [i] = (rangeMax [i] - rangeMin [i]) * scoutAreaRadius;
  }

  double sqr = 0.0;
  for (int i = 0; i < coordinates; i++) sqr += areasRadius [i] * areasRadius [i];
  hypersphereRadius  = MathSqrt (sqr) * collectionRadius;

  distHyperspCenters = hypersphereRadius * 2.0;

  sqr = 0.0;
  for (int i = 0; i < coordinates; i++) sqr += scoutRadius [i] * scoutRadius [i];
  scoutHyperspRadius = MathSqrt (sqr) * scoutAreaRadius;

  ArrayResize (indB, beesNumber);
  ArrayResize (valB, beesNumber);

  ArrayResize (bees,  beesNumber);
  ArrayResize (beesT, beesNumber);
  for (int i = 0; i < beesNumber; i++)
  {
    ArrayResize (bees  [i].c, coordinates);
    ArrayResize (beesT [i].c, coordinates);
    bees  [i].n = -DBL_MAX;
    beesT [i].n = -DBL_MAX;
  }
}
//——————————————————————————————————————————————————————————————————————————————

最简单也是开放的类方法就是给蜜蜂分派任务。 这里的一切都很简单。 如果有尚未探索区域,则重置整个蜂巢的花蜜值,并启动区域标记。 我们在每个世代上调用该方法,直到获得适应度函数的值。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ABC::TasksForBees ()
{
  if (!scouting)
  {
    nB = -DBL_MAX;
  }
  MarkUpAreas ();
}
//——————————————————————————————————————————————————————————————————————————————

第二个公开方法在每个世代调用。 在获取适应度函数的值后执行启动。 在这种情况下,如果尚未进行探索,我们按花蜜值对蜜蜂进行排序,并将列表中第一批蜜蜂的坐标和花蜜数量复制到相应的区域中。 如果地点勘探已经进行,那么我们复制采集花蜜区域的坐标和花蜜数量,前提是结果有所改善。 此外,更新整个蜂巢的最佳结果。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ABC::CollectingNectar ()
{
  if (!scouting)
  {
    SortingBees ();

    for (int a = 0; a <areasNumber; a++)
    {
      ArrayCopy (areas [a].cB, bees [a].c, 0, 0, WHOLE_ARRAY);
      areas [a].n = bees [a].n;
    }

    scouting = true;
  }
  else
  {
    //transfer the nectar to the hive---------------------------------------------
    for (int b = 0; b < beesNumber; b++)
    {
      if (bees [b].n > areas [bees [b].aInd].n)
      {
        ArrayCopy (areas [bees [b].aInd].cB, bees [b].c, 0, 0, WHOLE_ARRAY);
        areas [bees [b].aInd].n = bees [b].n;
      }

      if (bees [b].n > nB)
      {
        ArrayCopy (cB, bees [b].c, 0, 0, WHOLE_ARRAY);
        nB = bees [b].n;
      }
    }

    SortingAreas ();
  }
}
//——————————————————————————————————————————————————————————————————————————————

MarkUpAreas () 方法值得详细研究。 我们将代码分解成多个部分。

在探索区域之前,没有关于寻找花朵采集花蜜的任何信息,我们将派遣所有蜜蜂进行初步探索。 在这个阶段,所有的蜜蜂都扮演着侦察员的角色。 由于没有关于花蜜的信息,我们向随机方向派遣侦察员,生成的随机数均匀分布在坐标范围内。

//if the areas is not scouting - send all the bees to scouting----------------
if (!scouting)
{
  for (int b = 0; b < beesNumber; b++)
  {
    for (int c = 0; c < coordinates; c++)
    {
      bees [b].c [c] = RNDfromCI (rangeMin [c], rangeMax [c]);
      bees [b].c [c] = SeInDiSp  (bees [b].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值