English Русский Deutsch 日本語 Português
preview
Algoritmos de optimización de la población: Objetos artificiales de búsqueda multisocial (artificial Multi-Social search Objects, MSO)

Algoritmos de optimización de la población: Objetos artificiales de búsqueda multisocial (artificial Multi-Social search Objects, MSO)

MetaTrader 5Probador | 31 julio 2024, 16:41
17 0
Andrey Dik
Andrey Dik

Contenido:

1. Introducción
2. Descripción del algoritmo
3. Resultados de las pruebas


1. Introducción

En el artículo anterior, analizamos la evolución de grupos sociales que se movían libremente en el espacio de búsqueda. Sin embargo, en este artículo proponemos cambiar este concepto y suponer que los grupos se mueven entre sectores saltando de uno a otro. Todos los grupos tienen sus centros, que se actualizan en cada iteración del algoritmo. Asimismo, introduciremos la noción de memoria tanto para el grupo en su conjunto como para cada partícula individual que lo compone. Con estos cambios, nuestro algoritmo permitirá ahora a los grupos pasar de un sector a otro basándose en la información sobre las mejores soluciones.

Esta nueva modificación abrirá nuevas posibilidades para el estudio de la evolución de los grupos sociales. El paso a otros sectores permitirá a los grupos compartir información y conocimientos dentro de cada sector, lo cual puede volver más eficaces la búsqueda y la adaptación. La introducción de la memoria permitirá a los grupos retener información sobre los movimientos anteriores y utilizarla para tomar decisiones sobre los movimientos futuros.

En este artículo, realizaremos una serie de experimentos para investigar cómo afectan estos nuevos conceptos al rendimiento de búsqueda del algoritmo. Además, analizaremos la interacción entre los grupos, su capacidad de cooperación y coordinación, así como su capacidad de aprendizaje y adaptación. Nuestros resultados pueden arrojar luz sobre la evolución de los sistemas sociales y ayudarnos a comprender mejor cómo los grupos se forman, evolucionan y se adaptan a entornos cambiantes.

Por último, se analizaremos las posibilidades de mejora del algoritmo modificado y su aplicación en diversos campos. Las modificaciones introducidas en el presente artículo nos permitirán explorar el espacio de soluciones y encontrar valores óptimos de forma más eficiente. Esto podría resultar de ayuda a los investigadores y profesionales que trabajan en el campo de la optimización y la búsqueda de soluciones.


2. Descripción del algoritmo

El objetivo último de un algoritmo que integra los principios de los grupos sociales es crear un sistema eficaz de coordinación y cooperación entre los miembros del grupo. Aquí tenemos los principios generales que pueden integrarse en un algoritmo de este tipo:

  • Algoritmo de desplazamiento: permite al grupo moverse entre diferentes sectores o áreas. Esto permitirá al grupo explorar una amplia variedad de recursos y experiencias, y compartir no solo información sobre coordenadas individuales, sino metadatos en forma de sectores con otros grupos.
  • Reparto de papeles y especialización: ofrece a los miembros del grupo la oportunidad de elegir determinadas áreas y especializarse en ellas. Esto permitirá al grupo usar eficazmente sus recursos y capacidades para alcanzar objetivos comunes, cosa que puede resultar especialmente útil al resolver problemas con espacios multidimensionales, en los que existen simultáneamente diferentes superficies de funciones (tanto suaves como discretas).
  • Cooperación y colaboración: permite a los miembros del grupo cooperar e interactuar entre sí. Esto puede incluir el intercambio de información y la discusión de ideas, así como la interpolación y la extrapolación en áreas desconocidas.
  • Conflicto y resolución de conflictos: mecanismos para resolver conflictos dentro de un grupo. Esto puede incluir el establecimiento de normas y procedimientos, además de la mediación y el diálogo para resolver diferencias y disputas. Por ejemplo, para evitar que las partículas compitan por los mismos sitios y permitirles ahorrar valiosas iteraciones del algoritmo.
  • Liderazgo y organización: posibilidad de contar en un grupo con líderes que aporten organización, coordinación y toma de decisiones. Los líderes deben ser capaces de motivar y dirigir al grupo para que logre sus objetivos.
  • Compartir conocimientos y experiencias: permitir que el grupo comparta activamente conocimientos y experiencias entre los participantes. Esto ayudará al grupo a aprender de los demás, adaptarse a nuevas situaciones y tomar decisiones con mayor conocimiento de causa. Capacidad de construir relaciones lógicas complejas entre coordenadas, en lugar de limitarse a una exploración estocástica del espacio.
  • Memoria de grupo: esto permitirá al grupo retener información sobre movimientos anteriores, roles, especializaciones, cooperación y resolución de conflictos. Esto permitirá al grupo aprovechar las lecciones aprendidas para tomar decisiones más informadas sobre futuros desplazamientos e interacciones.

La integración de estos principios generales en un algoritmo creará un sistema social más eficaz, capaz de cooperar, coordinarse, compartir información y adaptarse al entorno cambiante.

Para una mejor comprensión en el contexto del algoritmo, explicaremos el significado de sector. El sector es una parte de la zona de definición del parámetro optimizado (ejes de coordenadas del espacio multidimensional). La sectorización de los ejes será la misma para todos los grupos; los dos grupos G0 y G1 podrán situarse en los sectores de las coordenadas correspondientes, por ejemplo, de la forma siguiente:

G0 (X) |---V---|-------|-------|-------|-------|

G0 (Y) |-------|---V---|-------|-------|-------|

G0 (Z) |-------|-------|-------|-------|---V---|

-----------------------------------------------------

G1 (X) |-------|-------|---V---|-------|-------|

G1 (Y) |---V---|-------|-------|-------|-------|

G1 (Z) |-------|-------|-------|---V---|-------|

Una de las ideas principales del algoritmo consiste en permitir que los grupos compartan conocimientos sobre los sectores exitosos, conservando al mismo tiempo cierta estocasticidad en la libertad de selección de los sectores.

Vamos ahora a describir el primer concepto de nuestro algoritmo. Primero analizaremos un algoritmo con grupos que se desplazan aleatoriamente por los sectores, sin tener en cuenta la memoria de las mejores soluciones.

El pseudocódigo del algoritmo será el siguiente:

1. Seleccionamos aleatoriamente los sectores para los grupos
2. Creamos puntos uniformemente en todos los sectores
3. Calculamos la función de aptitud
4. Actualizamos la solución global (mejor población de partículas)
5. Obtenemos el valor de la mejor partícula del grupo en la iteración actual
6. Actualizamos la memoria de las mejores soluciones sobre los sectores para cada grupo.
7. Actualizamos la memoria de las mejores soluciones sobre los sectores para cada partícula.
8. Actualizamos la mejor solución por grupo
9. Para un grupo, preguntamos para cada coordenada por separado a otro grupo si su solución es la mejor:
9. a) en caso afirmativo : tomaremos su sector
9. b) en caso negativo: con probabilidad de elegir otro sector
10. Creamos partículas según las probabilidades:
10. a) en caso afirmativo : de manera uniforme en todo el sector
10. (b) En caso negativo: aclararemos la decisión del grupo
11. Repetimos desde el paso 4.

Podemos visualizar la arquitectura interna del grupo y el marcado de coordenadas por sectores de la manera siguiente:

Group  [groups]|
                        |-----------------------------------------
                        |fB
                        |-----------------------------------------
                        |fBLast
                        |-----------------------------------------
                        |cB              [coords]
                        |-----------------------------------------
                        |cLast         [coords]
                        |-----------------------------------------
                        |centre       [coords]
                        |-----------------------------------------
                        |secInd       [coords]
                        |-----------------------------------------
                        |secIndLast [coords]
                        |-----------------------------------------
                        |p                [groupSize]|
                        |                                    |-------------------
                        |                                    |c   [coords]
                        |                                    |f   

m [coords]|
                 |--------------
                 |min [sectNumb]
                 |max [sectNumb]

Vamos a pasar a la descripción del código.

Para marcar el espacio de búsqueda por sectores necesitaremos especificar los límites de los sectores, para ello escribiremos la estructura "S_Min_Max".  Vamos a desglosarla pieza por pieza:

La estructura "S_Min_Max" tiene dos campos de datos: "min" y "max", que representan el límite del sector a la izquierda, y "max" representa el límite del sector a la derecha. El tamaño de ambos arrays será igual al número de sectores, que se establecerá mediante el parámetro "sectNumb".

La estructura también definirá la función "Init", que inicializará los arrays "min" y "max". Tomará el parámetro "sectNumb", que especifica el número de sectores. Dentro de la función "Init", los arrays "min" y "max" se redimensionarán según el parámetro "sectNumb" transmitido. Así, esta estructura permitirá almacenar los límites de los sectores e inicializarlos usando la función "Init".

//——————————————————————————————————————————————————————————————————————————————
struct S_Min_Max
{
  void Init (int sectNumb)
  {
    ArrayResize (min, sectNumb);
    ArrayResize (max, sectNumb);
  }
  double min []; //sector border on the left, size - number of sectors
  double max []; //sector border on the right, size - number of sectors
};
//——————————————————————————————————————————————————————————————————————————————

Para describir una partícula, un miembro del grupo, escribiremos la estructura "S_Particle", que contiene dos campos: "c" y "f".

  • "c" será un array para almacenar las coordenadas de la partícula. El tamaño del array vendrá determinado por el parámetro "coords" transmitido a la función "Init".
  • "f" será el valor de la función de aptitud de la partícula inicializada con el número "-DBL_MAX" en la función "Init".

Así, esta estructura ofrecerá un contenedor para almacenar las coordenadas de una partícula y su valor de característica asociado.

//——————————————————————————————————————————————————————————————————————————————
struct S_Particle
{
  void Init (int coords, int sectNumb)
  {
    ArrayResize (c, coords);

    f = -DBL_MAX;
  }

  double c  [];
  double f;
};
//——————————————————————————————————————————————————————————————————————————————

Fusionaremos el grupo de partículas en la estructura de datos "S_Group". La estructura "S_Group" contendrá varios campos de datos:

  • "p" representará un array de estructuras "S_Particle", que se utilizará para almacenar las partículas del grupo. El tamaño del array "p" vendrá determinado por el parámetro "groupSize" transmitido a la función "Init". Dentro del ciclo "for", cada partícula se inicializará utilizando la función "Init" de la estructura "S_Particle".
  • "secInd" y "secIndLast" serán los arrays que almacenarán los índices de sector en cada coordenada. El tamaño de los arrays "secInd" y "secIndLast" vendrá determinado por el parámetro "coords".
  • "cB" y "cBLast" serán los arrays que almacenarán las mejores coordenadas del grupo y las mejores coordenadas anteriores, respectivamente. El tamaño de los arrays "cB" y "cBLast" también vendrá determinado por el parámetro "coords".
  • "fB" y "fBLast" serán las variables que almacenarán el mejor resultado y el mejor resultado anterior del grupo, respectivamente.
  • "centre" será el array que almacenará el centro. El tamaño del array "centre" también vendrá determinado por el parámetro "coords" y se utilizará para determinar las mejores coordenadas por sectores para todo el grupo.

La función "Init" inicializará todos los arrays y variables de la estructura "S_Group". Tomará tres parámetros: "coords" - número de coordenadas, "groupSize" - tamaño del grupo, "sectNumb" - número de sectores.

Así, esta estructura ofrecerá un contenedor para almacenar la información sobre un grupo de partículas. Las partículas obedecen las reglas de grupo y no interactúan con partículas de otros grupos: la interacción se producirá por transferencia indirecta de información a través de los sectores a nivel del grupo.

//——————————————————————————————————————————————————————————————————————————————
struct S_Group
{
  void Init (int coords, int groupSize, int sectNumb)
  {
    ArrayResize     (p,             groupSize);
    ArrayResize     (secInd,        coords);
    ArrayResize     (cB,            coords);
    ArrayResize     (cBLast,        coords);
    ArrayResize     (secIndLast,    coords);
    ArrayResize     (centre,        coords);
    for (int i = 0; i < groupSize; i++)  p [i].Init (coords, sectNumb);

    fB     = -DBL_MAX;
    fBLast = -DBL_MAX;
  }

  S_Particle p          [];
  int        secInd     []; //sector index on the coordinate, size is the number of coordinates
  int        secIndLast []; //previous index of the sector on the coordinate, the size is the number of coordinates

  double     cB         []; //the best coord's in the group
  double     cBLast     []; //the previous best coord's in the group
  double     fB;            //the best result in the group
  double     fBLast;        //the previous best result in the group
  double     centre [];
};
//——————————————————————————————————————————————————————————————————————————————

Describiremos el agente de optimización con la estructura "S_Agent", que se usará para realizar la transferencia de información desde las partículas de los grupos hasta el cálculo de la función aptitud. La estructura "S_Agent" contendrá dos campos:

  • "c" - array que almacenará las coordenadas del agente.
  • "f" - almacenará la función de aptitud del agente.

La función "Init" inicializará el array "c" y la variable "f" en la estructura "S_Agent". Tomará el parámetro "coords" que definirá el tamaño del array "c".

//——————————————————————————————————————————————————————————————————————————————
struct S_Agent
{
  void Init (const int coords)
  {
    ArrayResize (c, coords);
    f = -DBL_MAX;
  }

  double c []; //coordinates
  double f;    //fitness
};
//——————————————————————————————————————————————————————————————————————————————

Describiremos el algoritmo multisocial usando la clase "C_AO_MSO". La clase contendrá varios campos de datos y métodos:

  • "cB" - array que almacenará las mejores coordenadas.
  • "fB" - variable que almacenará la aptitud de las mejores coordenadas.
  • "a" - array de estructuras "S_Agent" que almacenará los agentes.
  • "rangeMax", "rangeMin" y "rangeStep" - arrays que almacenarán los rangos de búsqueda máximo y mínimo y el paso, respectivamente.

La clase también contendrá varios métodos:

  • "Init" inicializará todos los miembros de datos de la clase. Tomará los siguientes parámetros: número de coordenadas "coordinatesNumberP", tamaño de la población "populationSizeP", número de grupos "groupsP", número de sectores "sectorsNumberP" en una coordenada, probabilidad de un sector aleatorio "probRNSsectorP", probabilidad de distribución uniforme "probUniformSectorP" para una partícula, probabilidad de concretar el resultado del grupo "probClgroupP" y el grado "powerP" para la función de distribución de ley de potencias.
  • "Moving" y "Revision" son métodos para realizar operaciones básicas en grupos y partículas de grupos.

En general, la clase "C_AO_MSO" supone la implementación de un algoritmo de optimización que utilizará la búsqueda múltiple con asignación óptima de agentes. Contendrá los datos y métodos necesarios para gestionar agentes, grupos y sectores, y también para realizar operaciones de búsqueda y mejora de resultados.

//——————————————————————————————————————————————————————————————————————————————
class C_AO_MSO
{
  //----------------------------------------------------------------------------
  public: double cB [];         //best coordinates
  public: double fB;            //FF of the best coordinates
  public: S_Agent a [];         //agents

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

  public: void Init (const int    coordinatesNumberP,  //coordinates number
                     const int    populationSizeP,     //population size
                     const int    groupsP,             //number of groups
                     const int    sectorsNumberP,      //sectors number
                     const double probRNSsectorP,      //probability random sector
                     const double probUniformSectorP,  //probability uniform distribution
                     const double probClgroupP,        //probability of clarifying the group's result
                     const double powerP);             //power

  public: void Moving   ();
  public: void Revision ();

  //----------------------------------------------------------------------------
  private: int    coords;                //coordinates number
  private: int    popSize;               //population size

  private: int    sectNumb;              //sectors number
  private: double sectorSpace [];        //sector space

  private: S_Group    gr [];             //groups
  private: S_Min_Max  min_max_Sector []; //sector boundary by coordinates

  private: int    groups;                //number of groups
  private: int    sectorsNumber;         //sectors number
  private: double probRNSsector;         //probability random sector
  private: double probUniformSector;     //probability uniform distribution
  private: double probClgroup;           //probability of clarifying the group's result
  private: double power;                 //power

  private: bool   revision;

  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: double PowerDistribution (const double In, const double outMin, const double outMax, const double power);
};
//——————————————————————————————————————————————————————————————————————————————

El método "Init" inicializará un objeto de la clase "C_AO_MSO" con los parámetros especificados. Veamos este código pieza por pieza:

Al principio de la función, se inicializarán las variables y los miembros de datos de la clase. El generador de números aleatorios se reiniciará utilizando la hora actual en microsegundos. A continuación, la variable "fB" se establecerá en el valor mínimo posible de tipo double "-DBL_MAX", mientras que la variable "revision" se establecerá en "false".

Los parámetros transmitidos a la función se asignarán entonces a los campos correspondientes de la clase.

Para distribuir las partículas de la población en grupos, se creará un array "partInSwarms" para almacenar el número de partículas de cada grupo. El tamaño de la array será igual al número de grupos. A continuación, la variable "particles" calculará el número de partículas de cada grupo dividiendo el tamaño total de la población "popSize" por el número de grupos "groups".

Si tenemos un resto "lost", se asignará a los grupos. El ciclo se ejecutará hasta que el resto sea 0.

A continuación, se redimensionarán los arrays y se inicializarán los objetos.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_MSO::Init (const int    coordinatesNumberP,  //coordinates number
                     const int    populationSizeP,     //population size
                     const int    groupsP,             //number of groups
                     const int    sectorsNumberP,      //sectors number
                     const double probRNSsectorP,      //probability random sector
                     const double probUniformSectorP,  //probability uniform distribution
                     const double probClgroupP,        //probability of clarifying the group's result
                     const double powerP)              //power
{
  MathSrand ((int)GetMicrosecondCount ()); // reset of the generator
  fB       = -DBL_MAX;
  revision = false;

  coords            = coordinatesNumberP;
  popSize           = populationSizeP;
  groups            = groupsP;
  sectNumb          = sectorsNumberP;
  probRNSsector     = probRNSsectorP;
  probUniformSector = probUniformSectorP;
  probUniformSector = probClgroupP;
  power             = powerP;

  //----------------------------------------------------------------------------
  int partInSwarms [];
  ArrayResize (partInSwarms, groups);

  int particles = popSize / groups;
  ArrayInitialize (partInSwarms, particles);

  int lost = popSize - particles * groups;

  if (lost > 0)
  {
    int pos = 0;

    while (true)
    {
      partInSwarms [pos]++;
      lost--;
      pos++;
      if (pos >= groups) pos = 0;
      if (lost == 0) break;
    }
  }

  //----------------------------------------------------------------------------
  ArrayResize (rangeMax,  coords);
  ArrayResize (rangeMin,  coords);
  ArrayResize (rangeStep, coords);
  ArrayResize (cB,        coords);

  ArrayResize (gr,        groups);
  for (int s = 0; s < groups; s++) gr [s].Init (coords, partInSwarms [s], sectNumb);

  ArrayResize (sectorSpace, coords);

  ArrayResize (a, popSize);
  for (int i = 0; i < popSize; i++) a [i].Init (coords);
}
//——————————————————————————————————————————————————————————————————————————————

El método "Moving" se encargará de desplazar las partículas en la primera iteración y se encargará de inicializar los grupos y sus partículas con la posición inicial.

Al principio de la función, se verificará el valor de la variable "revision" para comprobar que solo se ejecuta una vez, y si es igual a "false".

La primera parte del código se encargará de dividir el espacio en sectores y de inicializar el array "min_max_Sector". Para cada coordenada "c", el tamaño del sector "sectorSpace[c]" se calculará como la diferencia entre "rangeMax[c]" y "rangeMin[c]" dividida por el número de sectores "sectNumb". Luego se inicializarán los valores "min" y "max" del array "min_max_Sector" para cada coordenada y cada sector.

A continuación, las partículas se ordenarán en el espacio de búsqueda. Para cada grupo "s", se seleccionarán sectores aleatorios para cada coordenada. Los valores de índice de sector se almacenarán en el array "secInd" para cada grupo. A continuación, las partículas del grupo se distribuirán aleatoriamente en los sectores seleccionados. Para cada partícula "p" y cada coordenada "c", se seleccionará un valor aleatorio "cd" dentro de los valores mínimo y máximo del sector, y este valor se almacenará en las coordenadas de la partícula.

El último bloque de código se encargará de enviar las partículas a los agentes. Así, crearemos un contador "cnt" que se utilizará para enumerar los agentes. Después, para cada grupo "s" y cada partícula "p", los valores de las coordenadas de las partículas se copiarán en el array "a[cnt].c", donde "cnt" se incrementará después de cada copia.

Así, el método se encargará de la colocación inicial aleatoria de las partículas en el algoritmo de optimización, dividirá el espacio en sectores, seleccionará aleatoriamente sectores para cada grupo y distribuirá las partículas dentro de los sectores seleccionados. A continuación, las partículas se enviarán a los agentes para su posterior procesamiento.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_MSO::Moving ()
{
  if (!revision)
  {
    //marking up sectors--------------------------------------------------------
    ArrayResize (min_max_Sector, coords);

    for (int c = 0; c < coords; c++)
    {
      sectorSpace [c] = (rangeMax [c] - rangeMin [c]) / sectNumb;
      min_max_Sector [c].Init (sectNumb);

      for (int sec = 0; sec < sectNumb; sec++)
      {
        min_max_Sector [c].min [sec] = rangeMin [c] + sectorSpace [c] * sec;
        min_max_Sector [c].max [sec] = min_max_Sector [c].min [sec] + sectorSpace [c];
      }
    }

    //--------------------------------------------------------------------------
    int    sect    = 0;   //sector
    double sectMin = 0.0; //sector's min
    double sectMax = 0.0; //sector's max
    int    ind     = 0;   //index
    double cd      = 0.0; //coordinate

    for (int s = 0; s < groups; s++)
    {
      //select random sectors for the group-------------------------------------
      for (int c = 0; c < coords; c++)
      {
        ind = (int)(RNDfromCI (0, sectNumb));
        if (ind >= sectNumb) ind = sectNumb - 1;

        gr [s].secInd     [c] = ind;
        gr [s].secIndLast [c] = ind;
      }

      //random distribute the particles of the group within the sectors---------
      for (int p = 0; p < ArraySize (gr [s].p); p++)
      {
        for (int c = 0; c < coords; c++)
        {
          sect               = gr [s].secInd [c];
          cd                 = RNDfromCI (min_max_Sector [c].min [sect], min_max_Sector [c].max [sect]);
          gr [s].p [p].c [c] = SeInDiSp (cd, rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }

    //--------------------------------------------------------------------------
    //send particles to agents
    int cnt = 0;

    for (int s = 0; s < groups; s++)
    {
      for (int p = 0; p < ArraySize (gr [s].p); p++)
      {
        ArrayCopy (a [cnt].c, gr [s].p [p].c, 0, 0, WHOLE_ARRAY);
        cnt++;
      }
    }

    revision = true;
  }
}
//——————————————————————————————————————————————————————————————————————————————

Las principales operaciones de desplazamiento de los grupos y sus partículas por el espacio de búsqueda se realizarán mediante el método de "Revision":

  • Actualización de la mejor solución global comparando los valores de la función de aptitud de cada partícula con el mejor valor actual. Si el valor de la función objetivo para la partícula actual supera el mejor valor actual, se convertirá en el nuevo mejor valor y sus coordenadas se copiarán en la variable "cB".
  • Transferencia de los resultados de los agentes a las partículas e identificación de la mejor partícula de cada grupo. El valor de la función de aptitud de cada partícula se establecerá igual al valor de la función objetivo del agente correspondiente a esa partícula. Si el valor de la función de aptitud de una partícula supera el mejor valor actual del grupo, se convertirá en el nuevo mejor valor y sus coordenadas se copiarán en la variable "cB" del grupo.
  • Actualización de la mejor solución para cada grupo. Si el nuevo mejor valor del grupo supera al anterior, se convertirá en el mejor valor actual y sus coordenadas se copiarán en las variables "cBLast" y "secIndLast" del grupo.
  • Para cada coordenada de cada grupo, se comprobará si hay otro grupo con una solución mejor. Si existe tal grupo, el sector y el centro del grupo actual se actualizarán con los valores del sector y el centro del grupo con la mejor solución. Por lo demás, el sector y el centro permanecerán inalterados.
  • Creación de nuevas partículas basadas en probabilidades. Para cada grupo y cada partícula del grupo, se generarán nuevos valores de coordenadas basados en las probabilidades. La probabilidad de seleccionar una distribución uniforme o una distribución que utilice la función "PowerDistribution" vendrá determinada por los parámetros "probUniformSector" y "power".
  • Transferencia de las partículas creadas a los agentes para su uso posterior en la siguiente iteración del algoritmo de optimización.

El método realizará una actualización de la solución en cada iteración, utilizando la información sobre las mejores soluciones de los grupos y probabilidades para crear nuevas partículas.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_MSO::Revision ()
{
  //----------------------------------------------------------------------------
  //Update the best global solution
  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
    }
  }

  //----------------------------------------------------------------------------
  //Transfer the results from the agents to the particles
  //and get the value of the best particle in the group at the current iteration
  int cnt = 0;
  for (int s = 0; s < groups; s++)
  {
    gr [s].fB = -DBL_MAX;

    for (int p = 0; p < ArraySize (gr [s].p); p++)
    {
      gr [s].p [p].f = a [cnt].f;

      if (a [cnt].f > gr [s].fB)
      {
        gr [s].fB = a [cnt].f;
        ArrayCopy (gr [s].cB, a [cnt].c, 0, 0, WHOLE_ARRAY);
      }

      cnt++;
    }
  }

  int sector = 0;

  //----------------------------------------------------------------------------
  //Update the best solution for the group
  for (int s = 0; s < groups; s++)
  {
    if (gr [s].fB > gr [s].fBLast)
    {
      gr [s].fBLast = gr [s].fB;
      ArrayCopy (gr [s].cBLast, gr [s].cB, 0, 0, WHOLE_ARRAY);
      ArrayCopy (gr [s].secIndLast, gr [s].secInd, 0, 0, WHOLE_ARRAY);
    }

    ArrayCopy (gr [s].centre, gr [s].cBLast);
  }


  //----------------------------------------------------------------------------
  int    sect    = 0;     //sector
  double sectMin = 0.0;   //sector's min
  double sectMax = 0.0;   //sector's max
  int    ind     = 0;     //index
  double cd      = 0.0;   //coordinate

  for (int s = 0; s < groups; s++)
  {
    for (int c = 0; c < coords; c++)
    {
      if (RNDfromCI (0.0, 1.0) < 0.6)
      {
        ind = (int)(RNDfromCI (0, groups));
        if (ind >= groups) ind = groups - 1;

        if (ind == s) ind++;
        if (ind > groups - 1) ind = 0;

        if (gr [ind].fBLast > gr [s].fBLast)
        {
          gr [s].secInd [c] = gr [ind].secIndLast [c];
          gr [s].centre [c] = gr [ind].cBLast [c];
        }
      }
      else
      {
        if (RNDfromCI (0.0, 1.0) < probRNSsector)
        {
          ind = (int)(RNDfromCI (0, sectNumb));
          if (ind >= sectNumb) ind = sectNumb - 1;

          gr [s].secInd [c] = ind;
          sect = gr [s].secInd [c];

          cd = RNDfromCI (min_max_Sector [c].min [sect], min_max_Sector [c].max [sect]);
          gr [s].centre [c] = SeInDiSp (cd, rangeMin [c], rangeMax [c], rangeStep [c]);
        }
        else gr [s].secInd [c] = gr [s].secIndLast [c];
      }
    }
  }

  //----------------------------------------------------------------------------
  for (int s = 0; s < groups; s++)
  {
    for (int p = 0; p < ArraySize (gr [s].p); p++)
    {
      for (int c = 0; c < coords; c++)
      {
        sect = gr [s].secInd [c];

        if (RNDfromCI (0.0, 1.0) < probUniformSector)
        {
          cd = RNDfromCI (min_max_Sector [c].min [sect], min_max_Sector [c].max [sect]);
        }
        else
        {
           cd = PowerDistribution (gr [s].centre [c], min_max_Sector [c].min [sect], min_max_Sector [c].max [sect], power);
        }

        gr [s].p [p].c [c] = SeInDiSp (cd, rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
  }
  //----------------------------------------------------------------------------
  cnt = 0;

  for (int s = 0; s < groups; s++)
  {
    for (int p = 0; p < ArraySize (gr [s].p); p++)
    {
      ArrayCopy (a [cnt].c, gr [s].p [p].c, 0, 0, WHOLE_ARRAY);
      cnt++;
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

A continuación, analizaremos el mismo algoritmo pero con memoria añadida para el grupo y sus partículas, así como algunos otros cambios en la lógica de la estrategia de búsqueda.

Modificaremos el pseudocódigo del algoritmo para tener en cuenta la presencia de memoria en grupos y partículas:

  • 1. Seleccionamos aleatoriamente los sectores para los grupos
  • 2. Creamos puntos uniformemente en todos los sectores
  • 4. Calculamos FF
  • 5. Actualizamos la solución global (mejor población de partículas)
  • 6. Obtenemos el valor de la mejor partícula del grupo en la iteración actual
  • 7. Actualizamos la memoria de las mejores soluciones sobre los sectores para cada grupo.
  • 8. Actualizamos la memoria de las mejores soluciones sobre los sectores para cada partícula.
  • 9. Actualizamos la mejor solución por grupo
  • 10. Para un grupo, preguntamos para cada coordenada por separado a otro grupo si su solución es la mejor:
  • 10. a) en caso afirmativo : tomaremos su sector
  • 10. b) en caso negativo: con probabilidad de elegir otro sector
  • 11. Creamos partículas según las probabilidades:
  • 11. a) en caso afirmativo : de manera uniforme en todo el sector
  • 11. b) si no: (probabilidad ? (aclararemos la decisión del grupo) : (aclaremos nuestra decisión))
  • 12. Repetimos desde el paso 4.

En teoría, añadir memoria debería permitir al grupo almacenar información sobre movimientos anteriores y usarla para tomar decisiones sobre movimientos futuros. Esto puede ayudar a los grupos a adaptarse mejor a un entorno cambiante y a explorar el espacio de soluciones con mayor eficacia.

Haremos los siguientes cambios en la arquitectura interna del grupo:

Swarm [groups]|
                        |-----------------------------------------
                        |fB
                        |-----------------------------------------
                        |fBLast
                        |-----------------------------------------
                        |cB               [coords]
                        |-----------------------------------------
                        |cBLast        [coords]
                        |-----------------------------------------
                        |secInd        [coords]
                        |-----------------------------------------
                        |secIndLast [coords]
                        |-----------------------------------------
                        |sMemory    [coords]|
                        |                               |---------------
                        |                               |cB      [sectNumb]
                        |                               |fB      [sectNumb]
                        |-----------------------------------------
                        |p         [groupSize] |
                        |                              |-------------------
                        |                              |c       [coords]
                        |                              |f
                        |                              |pMemory [coords]|
                        |                                                            |--------
                        |                                                            |cB [sectNumb]
                        |                                                            |fB [sectNumb]

Luego añadiremos la estructura "S_Memory" que describe la memoria. Para grupos y partículas, la memoria tendrá el mismo aspecto y contendrá dos arrays "cB" y "fB" para almacenar la información sobre las mejores coordenadas y las funciones de aptitud para esas coordenadas.

//——————————————————————————————————————————————————————————————————————————————
struct S_Memory
{
  void Init (int sectNumb)
  {
    ArrayResize     (cB, sectNumb);
    ArrayResize     (fB, sectNumb);
    ArrayInitialize (fB, -DBL_MAX);
  }
  double cB []; //the best sector coordinate, size is the number of sectors
  double fB []; //FF is the best coordinate on a sector, size is the number of sectors
};
//——————————————————————————————————————————————————————————————————————————————

Como consecuencia, añadiremos declaraciones de memoria a las estructuras de partículas y grupos:

//——————————————————————————————————————————————————————————————————————————————
struct S_Particle
{
  <..............code is hidden.................>

  S_Memory pMemory []; //particle memory, size - the number of coordinates
};
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
struct S_Group
{
  <..............code is hidden.................>

  S_Memory sMemory []; //group memory, size - number of coordinates 
}; 
//——————————————————————————————————————————————————————————————————————————————

También hemos modificado el método "Revision":

  • Actualización de las mejores coordenadas de sector en la memoria del enjambre. Para cada grupo y cada coordenada, se buscarán todos los sectores. Si el valor de "fB" del grupo sobre el sector es mayor que el valor de "fB" en la memoria sobre el sector, "fB" y "cB" se actualizarán en la memoria.
  • Actualización de las mejores posiciones en la memoria de partículas. Si el valor de la función de aptitud de la partícula es mayor que el valor de la memoria de partículas, "fB" y "cB" se actualizarán en la memoria.
//——————————————————————————————————————————————————————————————————————————————
void C_AO_MSO::Revision ()
{
  //----------------------------------------------------------------------------
  //Update the best global solution
  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
    }
  }

  //----------------------------------------------------------------------------
  //Transfer the results from the agents to the particles
  //and get the value of the best particle in the group at the current iteration
  int cnt = 0;
  for (int s = 0; s < groups; s++)
  {
    gr [s].fB = -DBL_MAX;

    for (int p = 0; p < ArraySize (gr [s].p); p++)
    {
      gr [s].p [p].f = a [cnt].f;

      if (a [cnt].f > gr [s].fB)
      {
        gr [s].fB = a [cnt].f;
        ArrayCopy (gr [s].cB, a [cnt].c, 0, 0, WHOLE_ARRAY);
      }

      cnt++;
    }
  }

  //----------------------------------------------------------------------------
  //Update the best sector coordinates in the swarm's memory
  int sector = 0;
  for (int s = 0; s < groups; s++)
  {
    for (int c = 0; c < coords; c++)
    {
      sector = gr [s].secInd [c];

      if (gr [s].fB > gr [s].sMemory [c].fB [sector])
      {
        gr [s].sMemory [c].fB [sector] = gr [s].fB;
        gr [s].sMemory [c].cB [sector] = gr [s].cB [c];
      }
    }
  }

  //----------------------------------------------------------------------------
  //Update in the memory of the particles their best positions by sector
  sector  = 0;
  for (int s = 0; s < groups; s++)
  {
    for (int p = 0; p < ArraySize (gr [s].p); p++)
    {
      for (int c = 0; c < coords; c++)
      {
        sector = gr [s].secInd [c];

        if (gr [s].p [p].f > gr [s].p [p].pMemory [c].fB [sector])
        {
          gr [s].p [p].pMemory [c].fB [sector] = gr [s].p [p].f;
          gr [s].p [p].pMemory [c].cB [sector] = gr [s].p [p].c [c];
        }
      }
    }
  }

  //----------------------------------------------------------------------------
  //Update the best solution for the group
  for (int s = 0; s < groups; s++)
  {
    if (gr [s].fB > gr [s].fBLast)
    {
      gr [s].fBLast = gr [s].fB;
      ArrayCopy (gr [s].cBLast, gr [s].cB, 0, 0, WHOLE_ARRAY);
      ArrayCopy (gr [s].secIndLast, gr [s].secInd, 0, 0, WHOLE_ARRAY);
    }
  }

  //----------------------------------------------------------------------------
  int    sect    = 0;     //sector
  double sectMin = 0.0;   //sector's min
  double sectMax = 0.0;   //sector's max
  int    ind     = 0;     //index
  double cd      = 0.0;   //coordinate

  for (int s = 0; s < groups; s++)
  {
    for (int c = 0; c < coords; c++)
    {
      ind = (int)(RNDfromCI (0, groups));
      if (ind >= groups) ind = groups - 1;

      if (ind == s) ind++;
      if (ind > groups - 1) ind = 0;

      if (RNDfromCI (0.0, 1.0) < 0.6)
      {
        if (gr [ind].fBLast > gr [s].fBLast)                                       
        {
          gr [s].secInd [c] = gr [ind].secIndLast [c];
        }
      }
      else                                                                      
      {
        if (RNDfromCI (0.0, 1.0) < probRNSsector)
        {
          ind = (int)(RNDfromCI (0, sectNumb));
          if (ind >= sectNumb) ind = sectNumb - 1;

          gr [s].secInd [c] = ind;
          sect = gr [s].secInd [c];

          if (gr [s].sMemory [c].fB [sect] == -DBL_MAX)
          {
            cd = RNDfromCI (min_max_Sector [c].min [sect], min_max_Sector [c].max [sect]);
            gr [s].sMemory [c].cB [sect] = SeInDiSp (cd, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
        else gr [s].secInd [c] = gr [s].secIndLast [c];
      }
    }
  }

  //----------------------------------------------------------------------------
  for (int s = 0; s < groups; s++)
  {
    for (int p = 0; p < ArraySize (gr [s].p); p++)
    {
      for (int c = 0; c < coords; c++)
      {
        sect = gr [s].secInd [c];

        if (RNDfromCI (0.0, 1.0) < probUniformSector)
        {
          cd = RNDfromCI (min_max_Sector [c].min [sect], min_max_Sector [c].max [sect]);
        }
        else
        {
          if (RNDfromCI (0.0, 1.0) < probClgroup)
          {
            cd = PowerDistribution (gr [s].sMemory [c].cB [sect], min_max_Sector [c].min [sect], min_max_Sector [c].max [sect], power);
          }
          else
          {
            cd = PowerDistribution (gr [s].p [p].pMemory [c].cB [sect], min_max_Sector [c].min [sect], min_max_Sector [c].max [sect], power);
          }
        }

        gr [s].p [p].c [c] = SeInDiSp (cd, rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
  }

  //----------------------------------------------------------------------------
  //send the particles to the agents
  cnt = 0;

  for (int s = 0; s < groups; s++)
  {
    for (int p = 0; p < ArraySize (gr [s].p); p++)
    {
      ArrayCopy (a [cnt].c, gr [s].p [p].c, 0, 0, WHOLE_ARRAY);
      cnt++;
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————


3. Resultados de las pruebas

Hemos analizado los resultados de las pruebas del algoritmo sin memoria de partículas y observado que funciona bastante bien. El algoritmo se encuentra entre los diez primeros de la tabla de clasificación. Debemos señalar que este algoritmo sirve solo como ejemplo de la lógica, y si hubiera mostrado resultados sobresalientes, lo habría incluido en la tabla. También hay muchas posibilidades para seguir aplicando y modificando este algoritmo.

Aunque los resultados no son sobresalientes, el algoritmo ha mostrado una buena capacidad de búsqueda al explorar distintas áreas del espacio de búsqueda.

C_AO_MSO|60|30|9|0.05|0.05|10.0
=============================
5 Hilly's; Func runs: 10000; result: 0.9313358190790157
25 Hilly's; Func runs: 10000; result: 0.6649184286250989
500 Hilly's; Func runs: 10000; result: 0.3282041522365852
=============================
5 Forest's; Func runs: 10000; result: 0.9522099605531393
25 Forest's; Func runs: 10000; result: 0.5542256622730999
500 Forest's; Func runs: 10000; result: 0.08984352753493675
=============================
5 Megacity's; Func runs: 10000; result: 0.7899999999999998
25 Megacity's; Func runs: 10000; result: 0.33533333333333326
500 Megacity's; Func runs: 10000; result: 0.042983333333333325
=============================
All score: 4.68905 (52.1%)

Los siguientes resultados se refieren a la variante de grupos sociales con memoria, la degradación de los resultados globales del algoritmo es pequeña. No obstante, este algoritmo ocupa un lugar digno en lo alto de la tabla de clasificación. Esto indica el potencial del algoritmo y su capacidad para lograr resultados satisfactorios. La idea de considerar el concepto de intercambio de memoria no solo entre grupos, sino también entre sus partículas, es un paso lógico para mejorar el algoritmo. la introducción de esta interacción adicional puede dar lugar a nuevas ideas y estrategias. Como hemos mencionado antes en el artículo, hemos descrito varios escenarios de interacciones entre grupos sociales, y hemos incluido solo algunos de ellos. Esto significa que existen muchas posibilidades de modificar y mejorar el algoritmo.

C_AO_MSOm|60|30|9|0.1|0.9|0.1|10.0
=============================
5 Hilly's; Func runs: 10000; result: 0.9468984351872132
25 Hilly's; Func runs: 10000; result: 0.5865441453580522
500 Hilly's; Func runs: 10000; result: 0.3186653673403949
=============================
5 Forest's; Func runs: 10000; result: 0.9064162754293653
25 Forest's; Func runs: 10000; result: 0.43175851113448455
500 Forest's; Func runs: 10000; result: 0.06865408175918558
=============================
5 Megacity's; Func runs: 10000; result: 0.6783333333333333
25 Megacity's; Func runs: 10000; result: 0.213
500 Megacity's; Func runs: 10000; result: 0.03310000000000002
=============================
All score: 4.18337 (46.4%)

Hilly

  MSO en la función de prueba Hilly.

Forest

  MSO en la función de prueba Forest.

Megacity

  MSO en la función de prueba Megacity.


Conclusiones

A la vista de los razonamientos y resultados anteriores, podemos añadir lo siguiente:

Durante los experimentos, hemos comprobado que el algoritmo con memoria tiene un rendimiento ligeramente inferior al del algoritmo sin memoria. No obstante, esto no significa que el algoritmo con memoria sea menos prometedor. Lo más probable es que tengamos que modificar el concepto de intercambio de memoria entre grupos y partículas para mejorar sus resultados.

Además, cabe señalar que la incorporación de otros principios de interacción entre grupos sociales puede mejorar sustancialmente la eficacia del algoritmo con memoria. Introducir mecanismos de colaboración, coordinación y aprendizaje entre grupos podría suponer una mejora significativa del rendimiento y llevar al algoritmo a una de las primeras posiciones de nuestra tabla de clasificación.

En conclusión, el estudio ofrece una nueva conceptualización de la evolución de los grupos sociales basada en el movimiento entre sectores y el uso de la memoria. Estos conceptos abren nuevas posibilidades en el estudio de los sistemas sociales y su capacidad de cooperación, coordinación y adaptación. Cabe esperar que los resultados ayuden a comprender mejor cómo funcionan y evolucionan los grupos sociales en entornos sociales complejos, lo cual nos brindará la oportunidad de seguir investigando en este campo.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/14162

Archivos adjuntos |
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Red neuronal en la práctica: Recta secante Red neuronal en la práctica: Recta secante
Como se explicó en la parte teórica, necesitamos usar regresiones lineales y derivadas cuando trabajamos con redes neuronales. ¿Pero por qué? La razón es que la regresión lineal es una de las fórmulas más simples que existen. Básicamente, una regresión lineal es solo una función afín. Sin embargo, cuando hablamos de redes neuronales, no nos interesan los efectos de la recta de regresión lineal. Lo que nos interesa es la ecuación que genera dicha recta. La recta generada poco importa. ¿Pero sabes cuál es la ecuación principal que hay que comprender? Si no, lee este artículo para empezar a comprenderlo.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Factorización de matrices: lo básico Factorización de matrices: lo básico
Como el objetivo aquí es ser didáctico. Mantendré las cosas en su forma más sencilla. Es decir, implementaremos solo lo necesario: la multiplicación de matrices. Verás que esto será suficiente para simular la multiplicación de una matriz por un escalar. La gran dificultad que muchas personas tienen a la hora de implementar un código utilizando la factorización de matrices es que, a diferencia de una factorización escalar, donde en casi todos los casos el orden de los factores no altera el resultado, cuando se usan matrices, la cosa no es así.