Discussão do artigo "Algoritmos de otimização populacionais: salto de sapo embaralhado"

 

Novo artigo Algoritmos de otimização populacionais: salto de sapo embaralhado foi publicado:

O artigo apresenta uma descrição detalhada do algoritmo salto de sapo embaralhado (Shuffled Frog Leaping Algorithm, SFL) e suas capacidades na solução de problemas de otimização. O algoritmo SFL é inspirado no comportamento dos sapos em seu ambiente natural e oferece uma nova abordagem para a otimização de funções. O algoritmo SFL é uma ferramenta eficaz e flexível, capaz de lidar com diversos tipos de dados e alcançar soluções ótimas.

O algoritmo salto de sapo embaralhado (Shuffled Frog Leaping Algorithm, SFL) foi proposto por M. Eusuff e outros autores em 2003. Este algoritmo combina princípios do algoritmo memético e do algoritmo enxame de partículas, e seu desenvolvimento foi inspirado no comportamento de um grupo de sapos na busca por alimento.

Originalmente, o algoritmo SFL foi desenvolvido como um método metaheurístico para resolver problemas de otimização combinatória. Ele é baseado no uso de funções matemáticas e busca heurística informada.

O algoritmo SFL consiste em várias populações virtuais interativas de sapos, chamadas memplexos. Os sapos virtuais desempenham o papel de hospedeiros ou portadores de memes, aqui um meme representa uma unidade de evolução cultural. Em cada memplexo, ocorre uma busca local independente usando um método semelhante à otimização por enxame de partículas, mas com foco na busca local.

Para facilitar as explorações globais, os sapos virtuais são periodicamente embaralhados e reorganizados em novos memplexos usando um método semelhante ao algoritmo de evolução complexa embaralhada. Além disso, sapos virtuais aleatórios são gerados e substituídos na população para permitir a geração aleatória de informações aprimoradas.

O algoritmo salto de sapo embaralhado é um método eficaz para resolver problemas complexos de otimização e permite alcançar soluções ótimas em várias áreas de aplicação. Neste artigo, examinaremos os princípios fundamentais e a aplicação deste algoritmo, bem como suas vantagens e limitações.

Autor: Andrey Dik

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

Agradeço ao autor por ter feito um ótimo trabalho e pela oportunidade gratuita de usá-lo!


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

Estou analisando um pouco o código. E substituí essa beleza por um 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 #:

Analisando um pouco o código.

Se entendi corretamente, todas as implementações de algoritmos de otimização na forma de classes têm alguma interface comum não formatada. Em particular, a configuração da nuvem de pesquisa.

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

É possível formalizar isso na forma de alguma variante fácil de usar? Em termos gerais, há uma classe base, herdamos as implementações de todos os algoritmos dela e as usamos da mesma forma por meio de funções virtuais.

 
fxsaber #:

Obrigado ao autor por fazer um ótimo trabalho e pela oportunidade gratuita de usá-lo!

Obrigado, fico feliz que meu trabalho seja útil)))

fxsaber #:

Analisando um pouco o código. E substitui essa beleza por um horror.

interessante

fxsaber #:

Se entendi corretamente, todas as implementações de algoritmos de otimização como classes têm algum tipo de interface comum não formatada. Especificamente, a especificação da nuvem de pesquisa.

É possível formalizá-la em alguma variante fácil de usar? Em termos gerais, há uma classe base, herdamos as implementações de todos os algoritmos dela e as usamos da mesma forma por meio de funções virtuais.

1. sim, adotei a maneira mais simples e criei as condições de limite dos algoritmos na forma de membros abertos - matrizes.

2. em geral, embora haja uma enorme variedade de algoritmos de otimização em termos da lógica de seu trabalho, tentei torná-los uniformes; todos eles têm três etapas: inicialização, movimentação de agentes para novas posições e revisão e, entre as duas últimas, o cálculo do FF, o que permite a aplicação flexível de algoritmos em aplicativos de usuários.

3. eu tive a ideia de fazer todos os algoritmos como objetos filhos de uma classe genérica, mas isso impede que, para fins didáticos dos artigos, as variáveis sejam nomeadas e comentadas especificamente para cada algoritmo e ajudem a entender melhor a lógica.

4. Entretanto, é verdade que todos os algoritmos podem ser unificados e uniformizados graças ao trabalho realizado no ponto 2.


O rangeMax e outras condições de limite devem ser argumentos no método Init.

 
fxsaber #:


Estou analisando um pouco o código. Substituí essa beleza por um horror.

A criação de uma função sob solicitação deve ser feita na própria classe (sem danças de string e "amarração" em fragmentos correspondentes de nomes de classe e elementos de enumeração). Há uma variante do autor aqui. Aqui está outra - algo como isto:

Arquivo Functions.mqh:

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

Use em um 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 #:

De qualquer forma, a criação de uma função sob solicitação deve estar na própria classe (sem danças de string e "amarração" em fragmentos correspondentes de nomes de classe e elementos de enumeração). Há uma variante do autor aqui. Aqui está outra - algo parecido com isto:

Arquivo Functions.mqh:

Use em um script como este:

Dois argumentos contra essa opção.

  1. Recusar-se a especificar uma função por uma string - recusar-se a especificar as configurações do robô por meio do arquivo ini.
  2. Singleton - recusa de trabalho paralelo de vários robôs idênticos com configurações diferentes.
 
fxsaber #:

Dois argumentos contra essa opção.

  1. Recusa de especificar uma função por meio de uma string - recusa de especificar as configurações do robô por meio de um arquivo ini.
  2. Singleton - recusa de trabalho paralelo de vários robôs idênticos com configurações diferentes.

Eu a esbocei apenas porque, se for criar alguns truques, sem problemas em potencial - fui "fisgado" pelo uso de loop com pesquisa de string (!) - ela pode ser muito ineficiente em programas maiores.

Não apenas os parâmetros de string no arquivo ini, mas também outros tipos (embora representados por texto).

Um singleton em uma fábrica não tem problema. Singleton em um objeto de função - nesse caso, apenas para a operacionalidade do exemplo - você pode implementar a multiplicidade.

 
Stanislav Korotky #:

Eu apenas o esbocei porque, se vou criar algum truque, ele deve ser feito sem problemas em potencial - fui "fisgado" pelo uso de um loop com pesquisa de string (!) - ele pode ser muito ineficiente em programas maiores.

O arquivo ini não contém apenas parâmetros de cadeia de caracteres, mas também outros tipos de parâmetros (embora eles sejam representados por texto).

Um singleton em uma fábrica é aceitável. Singleton em um objeto de função - nesse caso, apenas para a operacionalidade do exemplo - você pode implementar a multiplicidade.

Eu uso a resolução de strings no estágio de inicialização. Acho que isso leva menos de um milissegundo. Sinceramente, não senti nenhum problema em potencial.

 
fxsaber #:

Obrigado ao autor por fazer um ótimo trabalho e pela oportunidade gratuita de usá-lo!



Estou analisando um pouco o código. E substituí essa beleza por um horror.

Qual é o ganho? É realmente horrível. Desculpe-me, se for o caso)))

 
Stanislav Korotky #:

De qualquer forma, a criação de uma função sob solicitação deve estar na própria classe (sem danças de string e "amarração" em fragmentos correspondentes de nomes de classe e elementos de enumeração). Há uma variante do autor aqui. Aqui está outra - algo parecido com isto:

Arquivo Functions.mqh:

Use em um script como este:

Desculpe, mas é claro que estou errado, bem ali naquela linha 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>()};
 

não criará um objeto de cada tipo?

...então como, apenas um tipo será usado durante todo o tempo do programa.

 
Dmitry Fedoseev #:

Qual é o ganho? Quero dizer, é realmente horrível. Peço desculpas se for o caso)))

Essa é uma pergunta difícil. Eu não sei.