Discusión sobre el artículo "Algoritmos de optimización de la población: Algoritmo de salto de rana aleatorio (Shuffled Frog-Leaping, SFL)"

 

Artículo publicado Algoritmos de optimización de la población: Algoritmo de salto de rana aleatorio (Shuffled Frog-Leaping, SFL):

El artículo presenta una descripción detallada del algoritmo de salto de rana aleatorio (SFL) y sus capacidades para resolver problemas de optimización. El algoritmo SFL se inspira en el comportamiento de las ranas en su entorno natural y ofrece un enfoque innovador para la optimización de características. El algoritmo SFL supone una herramienta eficaz y flexible que puede gestionar una gran variedad de tipos de datos y alcanzar soluciones óptimas.

El algoritmo de salto de rana aleatorio (SFL) fue propuesto por M. Eusuff y otros en 2003. Este algoritmo combina los principios del algoritmo memético y el algoritmo de enjambre de partículas, y su desarrollo se inspiró en el comportamiento de un grupo de ranas en busca de comida.

El algoritmo SFL se desarrolló originalmente como un método metaheurístico para resolver problemas de optimización combinatoria, y se basa en el uso de funciones matemáticas y en la búsqueda heurística informada.

El algoritmo SFL consta de varias poblaciones virtuales de ranas que interactúan entre sí: estas se denominan memeplexes. Las ranas virtuales desempeñan el papel de huéspedes o portadoras de memes, donde un meme representa una unidad de evolución cultural. Cada memeplex se busca localmente de forma independiente usando un método similar a la optimización por enjambre de partículas, pero haciendo hincapié en la búsqueda local.

Para permitir la investigación global, las ranas virtuales se barajan y reorganizan periódicamente en nuevos memeplexes usando un método similar al algoritmo de evolución compleja barajada. Además, se generarán ranas virtuales aleatorias y se sustituirán en la población para permitir la generación aleatoria de información mejorada.

El algoritmo SFL es un método eficaz para resolver problemas de optimización complejos y consigue soluciones óptimas en diversas aplicaciones. En este artículo repasaremos los principios básicos y la aplicación de este algoritmo, así como sus ventajas y desventajas.

Autor: Andrey Dik

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

¡Gracias al autor por hacer un gran trabajo y por la oportunidad gratuita de usarlo!


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));
  }
}

Estoy mirando un poco el código. Y reemplazado esta belleza con tal horror.

#property script_show_inputs

#include <Math\Functions.mqh> // https://www.mql5.com/ru/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 #:

Mirando un poco el código.

Si he entendido bien, todas las implementaciones de los algoritmos de optimización en forma de clases tienen alguna interfaz común sin formato. En particular, la configuración de la nube de búsqueda.

  public: double rangeMax  []; //maximum search range
  public: double rangeMin  []; //manimum search range
  public: double rangeStep []; //step search

¿Es posible formalizarlo en forma de alguna variante fácil de usar? A grandes rasgos, hay una clase base, heredamos de ella las implementaciones de todos los algoritmos, y luego los utilizamos de la misma manera a través de funciones virtuales.

 
fxsaber #:

¡Gracias al autor por hacer un gran trabajo y por la oportunidad gratuita de utilizarlo!

Gracias, me alegro de que mi trabajo sea útil)))

fxsaber #:

Mirando un poco el código. Y sustituido esta belleza por semejante horror.

interesante

fxsaber #:

Si he entendido bien, todas las implementaciones de algoritmos de optimización como clases tienen algún tipo de interfaz común sin formato. En concreto, la especificación de la nube de búsqueda.

¿Es posible formalizarla en alguna variante fácil de usar? A grandes rasgos, hay una clase base, heredamos de ella las implementaciones de todos los algoritmos, y luego los usamos de la misma manera a través de funciones virtuales.

1. sí, tomé el camino simple e hice las condiciones de contorno de los algoritmos en forma de miembros abiertos - arrays.

2. en general, aunque hay una gran variedad de algoritmos de optimización en términos de la lógica de su trabajo, traté de hacerlos todos uniformes, todos tienen tres pasos: inicialización, mover los agentes a nuevas posiciones y revisión, y entre los dos últimos, el cálculo de FF, esto permite una aplicación flexible de los algoritmos en las aplicaciones de usuario.

3. tuve la idea de hacer todos los algoritmos como objetos hijos de una clase genérica, pero esto impide a efectos didácticos de los artículos, las variables se nombran y comentan específicas de cada algoritmo y ayudan a entender mejor la lógica.

4. sin embargo, es cierto que todos los algoritmos pueden ser unificados y llevados a la uniformidad gracias al trabajo realizado en el punto 2.


El rangeMax y otras condiciones de contorno deben ser argumentos en el método Init.

 
fxsaber #:


Estoy mirando un poco el código. He sustituido esta belleza por semejante horror.

La creación de una función a petición debe hacerse en la propia clase (sin bailes de cadenas y "atar" en fragmentos coincidentes de nombres de clase y elementos de enumeración). Hay una variante del autor allí. Aquí hay otro - algo como esto

Functions.mqh archivo:

class C_Function; // forward declaration for use in FunctionInstance base class

class FunctionInstance // common parent for all fabrics to be used in unified manner, for example listed in an array
{
  protected:
    C_Function *instance;
  public:
    FunctionInstance(): instance(NULL) { }
    ~FunctionInstance()                    // destructor provides garbage collection
    {
      if(CheckPointer(instance) == POINTER_DYNAMIC) delete instance;
    }
    virtual C_Function *create() = 0;
};

template<typename T>
class FunctionFabric: public FunctionInstance // specific fabric
{
  public:
    virtual T *create() override
    {
      if(instance == NULL) instance = new T; // here dynamic "singleton" pattern is implemeted for simplicity only (could be a collection of objects or no references at all)
      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;
  }
  ...
};
...

Utilizar en un script como este:

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 #:

Por todos los medios, la creación de una función a petición debe estar en la propia clase (sin bailes de cadena y "atar" en fragmentos coincidentes de nombres de clase y elementos de enumeración). Hay una variante del autor allí. Aquí hay otro - algo como esto:

Functions.mqh archivo:

Usar en un script como este:

Dos argumentos en contra de esta opción.

  1. Rechazo de la especificación de una función mediante una cadena: rechazo de la especificación de la configuración del robot mediante un archivo ini.
  2. Singleton - rechazo del trabajo paralelo de varios robots idénticos con diferentes configuraciones.
 
fxsaber #:

Dos argumentos en contra de esta opción.

  1. Rechazo de especificar una función mediante una cadena - rechazo de especificar la configuración del robot mediante un archivo ini.
  2. Singleton - rechazo del trabajo paralelo de varios robots idénticos con diferentes configuraciones.

Lo esbocé sólo porque si voy a crear algunos trucos, entonces sin problemas potenciales - me "enganchó" el uso de bucle con búsqueda de cadena (!) - puede ser super ineficiente en programas más grandes.

No sólo los parámetros de cadena en el archivo ini, sino también otros tipos (aunque representados por texto).

Un singleton en una fábrica está bien. Singleton en objeto de función - en este caso sólo para la operatividad del ejemplo - puede implementar multiplicidad.

 
Stanislav Korotky #:

Sólo lo esbocé porque si voy a construir algún truco, debería ser sin problemas potenciales - me "enganchó" el uso de un bucle con búsqueda de cadenas (!) - puede ser super ineficiente en programas más grandes.

El ini-file no sólo contiene parámetros de cadena, sino también otros tipos de parámetros (aunque están representados por texto).

Un singleton en una fábrica está bien. Singleton en objeto función - en este caso sólo para la operatividad del ejemplo - se puede implementar la multiplicidad.

Yo uso la resolución de cadenas en la etapa de inicialización. Creo que se tarda menos de un milisegundo. Honestamente, no sentí ningún problema potencial.

 
fxsaber #:

¡Gracias al autor por hacer un gran trabajo y por la oportunidad gratuita de utilizarlo!



Estoy mirando un poco el código. Y reemplazado esta belleza con tal horror.

¿Cuál es la ganancia? Es realmente horrible. Lo siento, en todo caso)))

 
Stanislav Korotky #:

Por todos los medios, la creación de una función a petición debe estar en la propia clase (sin bailes de cadena y "atar" en fragmentos coincidentes de nombres de clase y elementos de enumeración). Hay una variante del autor allí. Aquí hay otro - algo como esto:

Functions.mqh archivo:

Usar en un script como este:

Lo siento, pero estoy equivocado, por supuesto, justo en esa línea de código:

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>()};
 

no creará un objeto de cada tipo?

... Entonces, ¿cómo, sólo un tipo se utilizará durante todo el tiempo del programa.

 
Dmitry Fedoseev #:

¿Cuál es la ganancia? Quiero decir, es realmente horrible. Pido disculpas en todo caso)))

Es una pregunta difícil. No lo sé.