文章 "群体优化算法:混合蛙跳算法(SFL)"

 

新文章 群体优化算法:混合蛙跳算法(SFL)已发布:

本文详细描述了混合蛙跳(Shuffled Frog-Leaping,SFL)算法及其在求解优化问题中的能力。SFL算法的灵感来源于青蛙在自然环境中的行为,为函数优化提供了一种新的方法。SFL算法是一种高效灵活的工具,能够处理各种数据类型并实现最佳解决方案。

混合蛙跳(SFL)算法是由M.Eusuff 和其他一些作者在2003年提出的。该算法结合了模因算法和粒子群算法的原理,其设计灵感来自一群青蛙在觅食过程中的行为。

SFL算法最初是作为一种求解组合优化问题的元启发式方法而开发的。它是基于数学函数和启发式搜索的使用。

SFL算法由几个相互作用的虚拟青蛙种群组成,称为模因复合体。虚拟青蛙是模因的宿主或载体,模因代表了文化进化的一个单元。每个模因复合体都使用类似于粒子群优化的方法进行独立的局部搜索,但重点是局部搜索。

为了支持全局探索,虚拟青蛙被周期性地混洗,并使用类似于混洗复杂进化(Shuffled Complex Evolution,SCE)算法的方法重组为新的模因。此外,随机虚拟青蛙在种群中被生成和替换,以允许随机生成改进的信息。

混合蛙跳是解决复杂优化问题的一种有效方法,它可以在各种应用领域实现最佳解决方案。在本文中,我们将介绍该算法的基本原理和应用,以及它的优点和局限性。

作者:Andrey Dik

 
К каждой статье я прикрепляю архив, содержащий обновленные актуальные версии кодов алгоритмов, описанных в предыдущих статьях.

感谢作者的出色工作和无偿使用的机会!


enum EFunc
{
  Skin,
  Forest,
  Megacity,
  Rastrigin,
  Universe
};
C_Function *SelectFunction (EFunc f)
{
  C_Function *func;
  switch (f)
  {
    case  Skin:
      func = new C_Skin (); return (GetPointer (func));
    case  Forest:
      func = new C_Forest (); return (GetPointer (func));
    case  Megacity:
      func = new C_Megacity (); return (GetPointer (func));
    case  Rastrigin:
      func = new C_Rastrigin (); return (GetPointer (func));
    case  Universe:
      func = new C_Universe (); return (GetPointer (func));
    
    default:
      func = new C_Skin (); return (GetPointer (func));
  }
}

我正在研究一下代码。并用这样一个恐怖的东西取代了这个美丽的东西。

#property script_show_inputs

#include <Math\Functions.mqh> //https://www.mql5.com/zh/articles/13366

template <typename T>
C_Function* New( const string &ClassName ) { return((typename(T) == ClassName) ? new T : NULL); }

C_Function* New2( string ClassName )
{  
  typedef C_Function* (*TNew)( const string& );
  static const TNew FuncNew[] = {New<C_Skin>, New<C_Forest>, New<C_Megacity>, New<C_Rastrigin>, New<C_Universe>};

  C_Function* Res = NULL;
  
  ClassName = "class " + ClassName;
  
  for (uint i = ArraySize(FuncNew); (Res == NULL) && (bool)i--;)
    Res = FuncNew[i](ClassName);  
    
  return(Res);
}

C_Function* SelectFunction2( const EFunc f )
{
  return(New2("C_" + EnumToString(f)));
}

input EFunc inFunc = Skin;

void OnStart()
{
  C_Function* Func = SelectFunction2(inFunc);
  
  if (Func != NULL)
  {
    Print(Func.GetNamFun());
    
    delete Func;
  }
}
 
fxsaber #:

稍微看一下代码

如果我没理解错的话,所有以类的形式实现的优化算法都有一些未格式化的通用接口。特别是设置搜索云。

  public: double rangeMax  []; //最大搜索范围
  public: double rangeMin  []; //最小搜索范围
  public: double rangeStep []; //步骤搜索

是否有可能将其形式化为某种易于使用的变体?粗略地说,有一个基类,我们从中继承所有算法的实现,然后通过虚拟函数以相同的方式使用它们。

 
fxsaber #:

感谢作者的出色工作和无偿使用的机会!

谢谢,很高兴我的工作有用)))

fxsaber#:

看了一下代码。把这个美丽的东西换成了这样一个恐怖的东西。

有趣

fxsaber#:

如果我没理解错的话,所有作为类的优化算法实现都有某种未格式化的通用接口。具体来说,就是搜索云规范。

是否有可能将其形式化为某种易于使用的变体?粗略地说,有一个基类,我们从中继承所有算法的实现,然后通过虚拟函数以相同的方式使用它们。

1. 是的,我采用了简单的方法,将算法的边界条件以开放成员(数组)的形式存在。

2. 一般来说,虽然优化算法的工作逻辑千差万别,但我试图将它们统一起来,它们都有三个步骤:初始化、将代理移动到新位置和修改,以及在最后两个步骤之间计算 FF,这样就可以在用户应用程序中灵活应用算法。

3. 我曾想过把所有算法都作为一个通用类的子对象,但这有碍于文章的教学目的,变量的命名和注释是针对每种算法的,有助于更好地理解其逻辑。

4. 不过,由于第 2 点所做的工作,所有算法确实都可以统一起来。


在 Init 方法中,rangeMax 和其他边界条件应该是参数。

 
fxsaber #:


我正在研究代码。我用这样一个恐怖的东西取代了这个美丽的东西。

根据请求创建函数应该在类本身中完成(不需要字符串舞蹈,也不需要 "绑定 "类名和枚举元素的匹配片段)。这里有作者的一个变体。下面是另一个版本--类似于这样

Functions.mqh 文件:

class C_Function; // 在 FunctionInstance 基类中使用的正向声明

class FunctionInstance // 以统一方式使用所有织物的共同母体,例如以数组形式列出
{
  protected:
    C_Function *instance;
  public:
    FunctionInstance(): instance(NULL) { }
    ~FunctionInstance()                    // 析构函数提供垃圾回收功能
    {
      if(CheckPointer(instance) == POINTER_DYNAMIC) delete instance;
    }
    virtual C_Function *create() = 0;
};

template<typename T>
class FunctionFabric: public FunctionInstance // 特定织物
{
  public:
    virtual T *create() override
    {
      if(instance == NULL) instance = new T; // 为简单起见,这里采用动态 "单例 "模式(可以是对象集合,也可以没有引用)
      return instance;
    }
};

//------------------------------------------------------------------------------
class C_Function
{
  public:
  template<typename T>
  static FunctionFabric<T> *fabric()
  {
    static FunctionFabric<T> singleton; // here static "singleton" is implemeted as most appropriate for fabric (no need to have more than 1 fabric per class)
    return &singleton;
  }
  ...
};
...

在类似这样的脚本中使用:

C_Function* NewX(const EFunc elem)
{
  // order of initialization corresponds to EFunc enumeration
  static FunctionInstance *pointers[] = {C_Function::fabric<C_Skin>(), C_Function::fabric<C_Forest>(), C_Function::fabric<C_Megacity>(), C_Function::fabric<C_Rastrigin>(), C_Function::fabric<C_Universe>()};
  return pointers[elem].create();
}

void OnStart()
{
  C_Function* Func = NewX(inFunc);
  
  if (Func != NULL)
  {
    Print(Func.GetNamFun());
    
    // delete Func; // not needed anymore, fabric will do itself
  }
}
 
Stanislav Korotky #:

按理说,根据请求创建函数应该在类本身中进行(而不是在类名和枚举元素的匹配片段上进行字符串舞蹈和 "绑定")。这里有作者的一个变体。这里还有另一种说法--类似于这样:

Functions.mqh 文件:

在这样的脚本中使用:

反对此选项的两个理由

  1. 拒绝通过字符串指定函数 - 拒绝通过 ini 文件指定机器人设置。
  2. 单机 - 拒绝多个相同的机器人以不同的设置并行工作。
 
fxsaber #:

反对这一方案的理由有两个。

  1. 拒绝通过字符串指定函数 - 拒绝通过内置文件指定机器人设置。
  2. 单机 - 拒绝多个相同的机器人以不同的设置并行工作。

我之所以草拟它,只是因为如果我要创建一些装置,那么在没有潜在问题的情况下--我被使用字符串搜索的循环 "迷住了"(!)--在大型程序中,它可能会超级低效。

不仅是 ini 文件中的字符串参数,还有其他类型的参数(虽然用文本表示)。

在工厂中使用单例是没有问题的。函数对象中的单例--在本例中只是为了例子的可操作性--可以实现多重性。

 
Stanislav Korotky #:

我只是画了个草图,因为如果我要制作一些装置,就应该没有潜在的问题--我被使用字符串搜索的循环 "迷住了"(!)--在大型程序中,它的效率可能超级低。

ini 文件不仅包含字符串参数,还包含其他类型的参数(尽管它们用文本表示)。

在工厂中使用单例就可以了。函数对象中的单例--在本例中只是为了示例的可操作性--可以实现多重性。

我在初始化阶段使用字符串解析。我认为这只需要不到一毫秒的时间。老实说,我没有感觉到任何潜在的问题。

 
fxsaber #:

感谢作者的出色工作和无偿使用的机会!



我正在研究一下代码。并用这样一个恐怖的东西取代了这个美丽的东西。

增益是多少?真的太可怕了。抱歉,如果有的话))

 
Stanislav Korotky #:

按理说,根据请求创建函数应该在类本身中进行(而不是在类名和枚举元素的匹配片段上进行字符串舞蹈和 "绑定")。这里有作者的一个变体。这里还有另一种说法--类似于这样:

Functions.mqh 文件:

在这样的脚本中使用:

对不起,我当然错了,就在那行代码中:

static FunctionInstance *pointers[] = {C_Function::fabric<C_Skin>(), C_Function::fabric<C_Forest>(), C_Function::fabric<C_Megacity>(), C_Function::fabric<C_Rastrigin>(), C_Function::fabric<C_Universe>()};
 

不会为每种类型创建一个对象?

......那么,如何在整个程序期间只使用一种类型?

 
Dmitry Fedoseev #:

有什么收获?我的意思是,这真的很糟糕。我向你道歉。)

这个问题很难回答我也不知道。