Algoritmos de optimización de la población: Algoritmo Boids, o algoritmo de comportamiento de bandada (Algoritmo Boids, Boids)
Contenido:
1. Introducción
2. Descripción del algoritmo
3. Resultados de las pruebas
1. Introducción
Al observar el comportamiento gregario de los animales en la naturaleza, admiramos casi sin percibirlo su coordinación coherente y eficaz. Como controlados por una fuerza invisible, se reorganizan sincrónicamente en un solo organismo, superando obstáculos y encontrando los caminos más óptimos. Esta fascinante armonía de movimientos basada en sencillas reglas de interacción ha servido de inspiración a numerosos algoritmos de optimización metaheurística.
Estudiando las complejas rutas migratorias de las bandadas de aves, los científicos han descubierto que siguen principios similares a los algoritmos de la inteligencia de enjambre. Así, los bancos de peces, como células vivas, forman estructuras pulsantes que recuerdan a los métodos de optimización basados en autómatas celulares. El comportamiento en manada de los ungulados, sus reacciones coordinadas ante el peligro y el vuelo sincronizado de un grupo de estorninos representan la increíble capacidad de los animales para trabajar juntos de forma coordinada.
Estos fascinantes ejemplos del mundo natural han inspirado la creación de potentes herramientas de optimización capaces de resolver problemas complejos, desde la planificación de rutas logísticas hasta el desarrollo de sistemas de gestión eficaces.
El algoritmo Boids (del inglés "boid" - abreviatura de "pájaro" y "oid", similitud, semejanza) es un algoritmo informático creado por Craig Reynolds en 1986, que simula el comportamiento de enjambres de animales, en particular, de bandadas de aves. El algoritmo se diseñó para imitar el comportamiento natural de los enjambres, en los que cada individuo se mueve siguiendo reglas sencillas, lo cual da lugar a un comportamiento colectivo. Se basa en las tres reglas que siguen:
1. Separación (Separation). Cada objeto (o "boid") intenta minimizar la posibilidad de colisión con los objetos cercanos.
2. Alineación (Alignment). Cada objeto tiende a que su dirección de movimiento sea la dirección media de movimiento de los objetos de su entorno.
3. Cohesión (Cohesion). Cada objeto tiende a tener una posición cercana a la posición media de los objetos de su entorno.
Estas tres sencillas reglas permiten modelar el complejo comportamiento de una bandada de pájaros o un enjambre de insectos. El algoritmo Boids se usa ampliamente en los gráficos de computadora para crear la animación de una bandada de pájaros, un enjambre de insectos o un banco de peces.
Inicialmente, la creación del algoritmo Boids tenía varios objetivos y aplicaciones:
1. Creación de animaciones realistas. El algoritmo Boids permite crear animaciones realistas de una manada de animales, lo cual ha servido como importante orientación para el desarrollo de los gráficos y la animación de computadora.
2. Modelación del comportamiento. Boids permite modelar comportamientos colectivos complejos a partir de reglas sencillas para cada individuo. Esto tiene aplicaciones en diversas esferas, como la investigación del comportamiento animal, la robótica o la gestión del tráfico, entre otras.
Un hecho interesante es que el algoritmo Boids ha servido de inspiración para el desarrollo de otros algoritmos: por ejemplo, el enjambre de partículas (PSO) y los algoritmos de modelado del comportamiento de las multitudes.
El algoritmo Boids es una herramienta popular para modelar el comportamiento colectivo y continúa siendo objeto de investigación y desarrollo en diversos campos de la ciencia y la tecnología.
En la siguiente animación podemos ver el comportamiento de esos mismos boids, que pueden agruparse en grupos compactos, volar separados y sincronizar su velocidad respecto a la de sus vecinos. Durante la grabación de la animación, los ajustes del algoritmo se han modificado sobre la marcha, lo cual nos ha permitido ver el efecto de los respectivos ajustes en el comportamiento de los boids.
Visualización del funcionamiento del algoritmo Boids.
Ventana de configuración del algoritmo Boids. Podemos acceder a ella usando la tecla F3. El parámetro "#reset" permite reiniciar el algoritmo aplicando un valor igual a "1".
2. Descripción del algoritmo
El algoritmo Boids, desarrollado por Craig Reynolds, se diseñó originalmente para modelar cualquier comportamiento de enjambre de animales, como bandadas de pájaros, enjambres de insectos y otros. Sin embargo, gracias a su flexibilidad y adaptabilidad, este algoritmo ha encontrado aplicaciones en diversos campos, como la optimización y la búsqueda. En el contexto de la optimización y la búsqueda, el algoritmo Boids puede usarse para resolver problemas que impliquen el comportamiento coordinado de un grupo de agentes. Por ejemplo, puede usarse para modelar el comportamiento de un enjambre que explora un territorio desconocido.
Sin embargo, debemos señalar que el algoritmo Boids no es en sí mismo un algoritmo de búsqueda en el sentido tradicional del término. No está diseñado para encontrar la solución óptima en un espacio dado de posibles soluciones, como hacen, por ejemplo, los algoritmos de descenso de gradiente o los algoritmos genéticos. En cambio, el algoritmo Boids modela el comportamiento de un grupo de agentes basándose en un conjunto de reglas simples, lo cual permite modelar comportamientos complejos y coordinados a nivel de grupo.
El algoritmo Boids puede adaptarse para la búsqueda distribuida de extremos de funciones. En este contexto, cada boid representa un agente de búsqueda que se desplaza por el espacio de posibles soluciones. En lugar de limitarse a seguir a otros boids, cada agente puede usar el valor de la característica en su posición actual para ajustar su movimiento o tener en cuenta la adaptabilidad de otros boids de la población.
Los trabajos de Craig Reynolds sobre el algoritmo Boids, que modela el comportamiento de los enjambres, inspiraron el concepto de inteligencia de enjambre. La inteligencia de enjambre describe el comportamiento colectivo de un sistema autoorganizado descentralizado y entre ellos se encuentra el algoritmo PSO.
Los sistemas de inteligencia de enjambre suelen estar formados por múltiples agentes (boids) que interactúan de forma local entre sí y con el entorno. Las ideas de comportamiento suelen proceder de la naturaleza y, concretamente, de los sistemas biológicos. Cada boid sigue unas reglas muy sencillas y, aunque no existe un sistema centralizado de control del comportamiento que indique a cada uno de ellos lo que debe hacer, las interacciones locales y, hasta cierto punto, aleatorias dan lugar a un comportamiento grupal inteligente no controlado por boids individuales.
El algoritmo Boids tiene bastantes parámetros externos, y cada uno de ellos influye significativamente en la naturaleza del movimiento de los boids. Veamos esos parámetros:
- cohesionWeight- peso de cohesión, determina la fuerza de atracción de los miembros de la manada entre sí.
- cohesionDist- distancia de cohesión, define la distancia entre los miembros de la manada a partir de la cual empiezan a sentirse atraídos entre sí.
- separationWeight - peso de separación, determina cuánto se alejarán entre sí los miembros de la manada.
- separationDist - distancia de separación, define la distancia entre los miembros de la manada a partir de la cual empezarán a alejarse unos de otros.
- alignmentWeight - peso de alineación, determina cuánto se alinearán los miembros de la manada en la dirección de movimiento de cada uno.
- alignmentDist - distancia de alineación, define la distancia entre los miembros de la manada en la que empezarán a alinearse en la dirección del movimiento de cada uno.
- minSpeed - velocidad mínima, el parámetro es necesario para evitar que los boids se detengan.
- maxSpeed - velocidad máxima a la que puede moverse un boid.
Es importante realizar una serie de experimentos para mejorar estos parámetros y obtener el comportamiento de enjambre deseado. Podemos empezar con los valores sugeridos e ir ajustándolos gradualmente para ver cómo afectan al comportamiento de la manada. Recuerde que no hay valores de parámetros "correctos" o "mejores" para el algoritmo Boids en un sentido absoluto, todo dependerá de la tarea y el escenario específicos.
1. La estructura contiene los siguientes campos:
- x[] - array para almacenar las coordenadas actuales del agente.
- dx[] - array de velocidades actuales del agente.
- m - masa del agente.
2. Init - método de la estructura "S_Boids_Agent", inicializa los campos de la estructura. Toma un argumento entero "coords" que se utilizará para redimensionar los arrays "x" y "dx" usando la función "ArrayResize".
Este código representa la estructura básica de datos para los agentes en el algoritmo Boids e inicializará sus campos cuando se cree un nuevo agente. Esto permitirá que cada agente tenga sus propias coordenadas y velocidades, que es un aspecto clave del algoritmo Boids. El campo "m" lo he añadido por propia iniciativa, para tener en cuenta la función de aptitud de la superficie cuando los boids se mueven, donde la masa es el valor de aptitud.
//—————————————————————————————————————————————————————————————————————————————— struct S_Boids_Agent { double x []; double dx []; double m; void Init (int coords) { ArrayResize (x, coords); ArrayResize (dx, coords); } }; //——————————————————————————————————————————————————————————————————————————————
Ahora vamos a definir la clase "C_AO_Boids" del algoritmo Boids, que es la sucesora de la clase básica de algoritmos poblacionales "C_AO" y contendrá los siguientes campos y métodos:
1. Campos públicos:
- ao_name - nombre del algoritmo de optimización.
- ao_desc - descripción del algoritmo de optimización.
- popSize - tamaño de la población.
- params - array de parámetros del algoritmo.
- cohesionWeight - peso de cohesión.
- cohesionDist - distancia de cohesión.
- separationWeight - peso de la separación.
- separationDist - distancia de separación.
- alignmentWeight - peso de la alineación.
- alignmentDist - distancia de alineación.
- maxSpeed - velocidad máxima.
- minSpeed - velocidad mínima.
- agente - vector de agentes.
2. Métodos:
- C_AO_Boids - constructor de clase que inicializa los campos de la clase.
- SetParams - método para establecer los parámetros del algoritmo.
- Init - método para inicializar el algoritmo. Soporta rangos de búsqueda mínimo y máximo, paso de búsqueda y número de épocas.
- Moving - método para desplazar a los agentes.
- Revision - método para revisar a los agentes.
3. Campos privados:
- distanceMax - distancia euclidiana máxima posible en el espacio de búsqueda.
- speedMax - velocidad máxima.
//—————————————————————————————————————————————————————————————————————————————— class C_AO_Boids : public C_AO { public: //-------------------------------------------------------------------- ~C_AO_Boids () { } C_AO_Boids () { ao_name = "Boids"; ao_desc = "Boids Algorithm"; ao_link = "https://www.mql5.com/ru/articles/14576"; popSize = 50; //population size cohesionWeight = 0.6; cohesionDist = 0.001; separationWeight = 0.005; separationDist = 0.03; alignmentWeight = 0.1; alignmentDist = 0.1; maxSpeed = 0.001; minSpeed = 0.0001; ArrayResize (params, 9); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "cohesionWeight"; params [1].val = cohesionWeight; params [2].name = "cohesionDist"; params [2].val = cohesionDist; params [3].name = "separationWeight"; params [3].val = separationWeight; params [4].name = "separationDist"; params [4].val = separationDist; params [5].name = "alignmentWeight"; params [5].val = alignmentWeight; params [6].name = "alignmentDist"; params [6].val = alignmentDist; params [7].name = "maxSpeed"; params [7].val = maxSpeed; params [8].name = "minSpeed"; params [8].val = minSpeed; } void SetParams () { popSize = (int)params [0].val; cohesionWeight = params [1].val; cohesionDist = params [2].val; separationWeight = params [3].val; separationDist = params [4].val; alignmentWeight = params [5].val; alignmentDist = params [6].val; maxSpeed = params [7].val; minSpeed = params [8].val; } bool Init (const double &rangeMinP [], //minimum search range const double &rangeMaxP [], //maximum search range const double &rangeStepP [], //step search const int epochsP = 0); //number of epochs void Moving (); void Revision (); void Injection (const int popPos, const int coordPos, const double value); //---------------------------------------------------------------------------- double cohesionWeight; //cohesion weight double cohesionDist; //cohesion distance double separationWeight; //separation weight double separationDist; //separation distance double alignmentWeight; //alignment weight double alignmentDist; //alignment distance double minSpeed; //minimum velocity double maxSpeed; //maximum velocity S_Boids_Agent agent []; private: //------------------------------------------------------------------- double distanceMax; double speedMax []; void CalculateMass (); void Cohesion (S_Boids_Agent &boid, int pos); void Separation (S_Boids_Agent &boid, int pos); void Alignment (S_Boids_Agent &boid, int pos); void LimitSpeed (S_Boids_Agent &boid); void KeepWithinBounds (S_Boids_Agent &boid); double Distance (S_Boids_Agent &boid1, S_Boids_Agent &boid2); }; //——————————————————————————————————————————————————————————————————————————————
El método "Init" de la clase "C_AO_Boids" se utilizará para inicializar las variables de la clase usando como base los parámetros transmitidos. Este método realizará una inicialización estándar usando el método "StandardInit", que admite los rangos de búsqueda mínimo y máximo, así como el paso de búsqueda.
Si la inicialización estándar tiene éxito, el método redimensionará el array "agent" a "popSize". Para cada elemento del "agente", se llamará al método "Init" con el parámetro "coords".
A continuación, el método calculará la distancia máxima y la velocidad máxima que se utilizarán en el algoritmo Boids para determinar el movimiento de los agentes.
Luego el método establecerá variables globales para diversos parámetros del algoritmo Boids, como el peso de cohesión, la distancia de cohesión, el peso de separación, la distancia de separación, el peso de alineación, la distancia de alineación, la velocidad máxima y la velocidad mínima.
El método retornará "true" si la inicialización se ha realizado correctamente y "false" en caso contrario.
Este método realizará la configuración inicial del algoritmo de optimización Boids con los parámetros especificados y lo preparará para realizar la optimización.
//—————————————————————————————————————————————————————————————————————————————— bool C_AO_Boids::Init (const double &rangeMinP [], //minimum search range const double &rangeMaxP [], //maximum search range const double &rangeStepP [], //step search const int epochsP = 0) //number of epochs { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //---------------------------------------------------------------------------- ArrayResize (agent, popSize); for (int i = 0; i < popSize; i++) agent [i].Init (coords); distanceMax = 0; ArrayResize (speedMax, coords); for (int c = 0; c < coords; c++) { speedMax [c] = rangeMax [c] - rangeMin [c]; distanceMax += pow (speedMax [c], 2); } distanceMax = sqrt (distanceMax); GlobalVariableSet ("#reset", 1.0); GlobalVariableSet ("1cohesionWeight", params [1].val); GlobalVariableSet ("2cohesionDist", params [2].val); GlobalVariableSet ("3separationWeight", params [3].val); GlobalVariableSet ("4separationDist", params [4].val); GlobalVariableSet ("5alignmentWeight", params [5].val); GlobalVariableSet ("6alignmentDist", params [6].val); GlobalVariableSet ("7maxSpeed", params [7].val); GlobalVariableSet ("8minSpeed", params [8].val); return true; } //——————————————————————————————————————————————————————————————————————————————
El método "Moving" de la clase "C_AO_Boids" se utilizará para mover los agentes durante el proceso de optimización. El método efectuará las siguientes acciones:
- Después se comprobará el valor de la variable global "reset". Si es 1.0, la bandera "revision" se establecerá en "false", el valor de la variable global "reset" se pondrá a 0.0, y el método finalizará.
- Los valores de los parámetros del algoritmo se extraerán de las variables globales.
- Si la bandera de "revisión" es "false", las coordenadas y velocidades de los agentes se inicializarán con valores aleatorios dentro de los rangos especificados. A continuación, la bandera "revisión" se establecerá en "true" y el método finalizará.
- Si el indicador de revisión no es igual a "false", se realizarán las siguientes acciones para cada agente:
- Los métodos "CalculateMass", "Cohesion", "Separation", "Alignment", "LimitSpeed" y "KeepWithinBounds" se llamarán para actualizar las velocidades del agente.
- Las coordenadas del agente se actualizarán en función de sus velocidades.
- Las coordenadas del agente se copiarán en el elemento "a" del array correspondiente.
/—————————————————————————————————————————————————————————————————————————————— void C_AO_Boids::Moving () { double reset = GlobalVariableGet ("#reset"); if (reset == 1.0) { revision = false; GlobalVariableSet ("#reset", 0.0); } cohesionWeight = GlobalVariableGet ("1cohesionWeight"); cohesionDist = GlobalVariableGet ("2cohesionDist"); separationWeight = GlobalVariableGet ("3separationWeight"); separationDist = GlobalVariableGet ("4separationDist"); alignmentWeight = GlobalVariableGet ("5alignmentWeight"); alignmentDist = GlobalVariableGet ("6alignmentDist"); maxSpeed = GlobalVariableGet ("7maxSpeed"); minSpeed = GlobalVariableGet ("8minSpeed"); if (!revision) { for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { agent [i].x [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); agent [i].dx [c] = (rangeMax [c] - rangeMin [c]) * u.RNDfromCI (-1.0, 1.0) * 0.001; a [i].c [c] = u.SeInDiSp (agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } //---------------------------------------------------------------------------- for (int i = 0; i < popSize; i++) { CalculateMass (); Cohesion (agent [i], i); Separation (agent [i], i); Alignment (agent [i], i); LimitSpeed (agent [i]); KeepWithinBounds (agent [i]); for (int c = 0; c < coords; c++) { agent [i].x [c] += agent [i].dx [c]; a [i].c [c] = u.SeInDiSp (agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } //——————————————————————————————————————————————————————————————————————————————
El método "CalculateMass" de la clase "C_AO_Boids" se utilizará para calcular la "masa" de cada agente en el algoritmo Boids. Eso es lo que ocurrirá con este método:
- Las variables "maxMass" y "minMass" se inicializarán para almacenar los valores máximo y mínimo de la función de aptitud entre todos los agentes.
- Para cada agente de la población, se comprobará si su función de aptitud "a[i].f" es mayor que el valor máximo actual "maxMass" y menor que el valor mínimo actual "minMass". En caso afirmativo, se actualizarán los valores máximo y mínimo correspondientes.
- Una vez probados todos los agentes, se calculará la "masa" de cada uno de ellos como el valor escalado de su función de aptitud en relación con los valores mínimo y máximo.
Este método ofrecerá un cálculo de la "masa" de cada agente en el algoritmo Boids; este no se encuentra en la lógica original del algoritmo Boids, y permite tener en cuenta la "superficie" del terreno sobre el que se desplazan los boids.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_Boids::CalculateMass () { double maxMass = -DBL_MAX; double minMass = DBL_MAX; for (int i = 0; i < popSize; i++) { if (a [i].f > maxMass) maxMass = a [i].f; if (a [i].f < minMass) minMass = a [i].f; } for (int i = 0; i < popSize; i++) { agent [i].m = u.Scale (a [i].f, minMass, maxMass, 0.0, 1.0); } } //——————————————————————————————————————————————————————————————————————————————
El método "Cohesion" de la clase "C_AO_Boids" se utilizará para implementar el comportamiento "cohesion" en el algoritmo Boids. Este comportamiento hará que los agentes se desplacen hacia el "centro de masa" de sus vecinos. El método ejecutará las siguientes acciones:
- La inicialización del array "centerX" para almacenar las coordenadas del centro de masa y la variable "numNeighbors" para contar el número de vecinos.
- Para cada agente de la población, se comprobará si no es él mismo (ya que un agente no debe considerarse a sí mismo como vecino) y si se encuentra a una distancia determinada (definida como "distanceMax * cohesionDist"). Si se cumplen ambas condiciones, las coordenadas del agente se añadirán a "centreX" y "numNeighbors" se incrementará en 1.
- Si se ha encontrado al menos un vecino, las coordenadas "centerX" se dividirán por el número de vecinos para obtener las coordenadas del centro de masa de los vecinos. La velocidad del agente "boid.dx" se corregirá entonces hacia el centro de masa para tener en cuenta el peso de cohesión "cohesionWeight".
Este método reforzará el comportamiento de "cohesión" del algoritmo Boids, que ayudará a los agentes a moverse juntos como grupo.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_Boids::Cohesion (S_Boids_Agent &boid, int pos) { double centerX []; ArrayResize (centerX, coords); ArrayInitialize (centerX, 0.0); int numNeighbors = 0; for (int i = 0; i < popSize; i++) { if (pos != i) { if (Distance (boid, agent [i]) < distanceMax * cohesionDist) { for (int c = 0; c < coords; c++) { centerX [c] += agent [i].x [c]; } numNeighbors++; } } } for (int c = 0; c < coords; c++) { centerX [c] /= numNeighbors; boid.dx [c] += (centerX [c] - boid.x [c]) * cohesionWeight; } } //——————————————————————————————————————————————————————————————————————————————
El método "Separation" de la clase "C_AO_Boids" se utilizará para implementar el comportamiento de "separación" en el algoritmo Boids. Este comportamiento hará que los agentes se muevan en dirección opuesta a los vecinos que están demasiado cerca para evitar colisiones. Eso es lo que ocurrirá con este método:
- El array "moveX" se inicializará para almacenar los cambios en las coordenadas del agente.
- Para cada agente de la población, se comprobará si no es él mismo (ya que un agente no debe considerarse a sí mismo como vecino) y si se encuentra a una distancia determinada (definida como "distanceMax * separationDist"). Si se cumplen ambas condiciones, la diferencia entre las coordenadas del agente actual y el vecino se añadirá a "moveX".
- Una vez comprobados todos los vecinos, se ajustará la velocidad del agente "boid.dx" en sentido contrario a "moveX", teniendo en cuenta el peso de separación "separationWeight".
Este método reforzará el comportamiento de "separación" del algoritmo Boids, que ayudará a los agentes a evitar colisiones entre sí.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_Boids::Separation (S_Boids_Agent &boid, int pos) { double moveX []; ArrayResize (moveX, coords); ArrayInitialize (moveX, 0.0); for (int i = 0; i < popSize; i++) { if (pos != i) { if (Distance (boid, agent [i]) < distanceMax * separationDist) { for (int c = 0; c < coords; c++) { moveX [c] += boid.x [c] - agent [i].x [c]; } } } } for (int c = 0; c < coords; c++) { boid.dx [c] += moveX [c] * separationWeight; } } //——————————————————————————————————————————————————————————————————————————————
El método "Alignment" de la clase "C_AO_Boids" se utilizará para implementar el comportamiento de "alineación" en el algoritmo Boids. Este comportamiento hará que los agentes se muevan en la misma dirección que sus vecinos. En este método se realizarán las siguientes acciones:
- La inicialización del array "avgDX" para almacenar la velocidad media de los vecinos y la variable "numVecinos" para contar el número de vecinos.
- Para cada agente de la población, se comprobará si no es él mismo (ya que un agente no debe considerarse a sí mismo como vecino) y si se encuentra a una distancia determinada (definida como "distanceMax * alignmentDist"). Si se cumplen ambas condiciones, la velocidad del vecino se añadirá a "avgDX" y "numNeighbors" se incrementará en 1.
- Si se ha encontrado al menos un vecino, las velocidades "avgDX" se dividirán por el número de vecinos para obtener la velocidad media. La velocidad del agente "boid.dx" se ajustará entonces en la dirección de la velocidad media para tener en cuenta el peso de alineación "alignmentWeight".
Este método reforzará el comportamiento de "alineación" del algoritmo Boids, que ayudará a los agentes a moverse juntos en la misma dirección.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_Boids::Alignment (S_Boids_Agent &boid, int pos) { double avgDX []; ArrayResize (avgDX, coords); ArrayInitialize (avgDX, 0.0); int numNeighbors = 0; for (int i = 0; i < popSize; i++) { if (pos != i) { if (Distance (boid, agent [i]) < distanceMax * alignmentDist) { for (int c = 0; c < coords; c++) { avgDX [c] += agent [i].dx [c]; } numNeighbors++; } } } for (int c = 0; c < coords; c++) { avgDX [c] /= numNeighbors; boid.dx [c] += (avgDX [c] - boid.dx [c]) * alignmentWeight; } } } //——————————————————————————————————————————————————————————————————————————————
El método "LimitSpeed" de la clase "C_AO_Boids" se utilizará para limitar la velocidad de los agentes en el algoritmo Boids. Este comportamiento impedirá que los agentes aumenten infinitamente su velocidad, lo cual no es natural para los animales reales. En este método ocurrirá lo siguiente:
- Se inicializará la variable "speed" para almacenar la velocidad actual del agente y la variable "d" para almacenar temporalmente los datos.
- Se calculará la velocidad actual del agente a partir de sus velocidades en cada coordenada.
- Para cada coordenada de agente, se realizarán los siguientes pasos:
- Se calculará "d" como la diferencia entre los valores máximo y mínimo del rango multiplicado por el factor de velocidad mínima.
- La velocidad del agente "boid.dx" se ajustará según la velocidad máxima posible "speedMax" y el factor de velocidad máxima "maxSpeed".
- Si el valor absoluto de la velocidad del agente es inferior a "d", la velocidad del agente se fijará en "d" (manteniendo el signo).
Este método impondrá un comportamiento de "limitación de velocidad" en el algoritmo Boids que ayudará a controlar la velocidad de los agentes y evitará que los boids dejen de moverse.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_Boids::LimitSpeed (S_Boids_Agent &boid) { double speed = 0; for (int c = 0; c < coords; c++) { speed += boid.dx [c] * boid.dx [c]; } speed = sqrt (speed); double d = 0; for (int c = 0; c < coords; c++) { d = (rangeMax [c] - rangeMin [c]) * minSpeed; boid.dx [c] = (boid.dx [c] / speed) * speedMax [c] * maxSpeed; if (fabs (boid.dx [c]) < d) { if (boid.dx [c] < 0.0) boid.dx [c] = -d; else boid.dx [c] = d; } } } //——————————————————————————————————————————————————————————————————————————————
El método "KeepWithinBounds" de la clase "C_AO_Boids" se utilizará para restringir el movimiento de los agentes dentro de los límites especificados en el algoritmo Boids. Este comportamiento impedirá que los agentes sobrepasen los límites especificados.
- Para cada coordenada del agente, se comprobará si está dentro de los límites especificados (definidos como "rangeMin" y "rangeMax").
- Si la coordenada del agente es menor que "rangeMin", se establecerá igual a "rangeMax", desplazando el agente al lado opuesto del límite.
- Si la coordenada del agente es mayor que "rangeMax", se establecerá igual a "rangeMin", moviendo el agente al lado opuesto del límite.
Este método impondrá una "restricción de límites" en el algoritmo Boids que ayudará a los agentes a mantenerse dentro de los límites dados.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_Boids::KeepWithinBounds (S_Boids_Agent &boid) { for (int c = 0; c < coords; c++) { double margin = 0; //(rangeMax [c] - rangeMin [c])* 0.00001; double turnFactor = (rangeMax [c] - rangeMin [c]) * 0.0001; /* if (boid.x [c] < rangeMin [c] + margin) { boid.dx [c] += turnFactor; } if (boid.x [c] > rangeMax [c] - margin) { boid.dx [c] -= turnFactor; } */ if (boid.x [c] < rangeMin [c]) { //boid.x [c] = rangeMax [c]; boid.dx [c] *= -1; } if (boid.x [c] > rangeMax [c]) { //boid.x [c] = rangeMin [c]; boid.dx [c] *= -1; } } } //——————————————————————————————————————————————————————————————————————————————
El método "Distance" de la clase "C_AO_Boids" se utilizará para calcular la distancia euclídea entre dos agentes en el algoritmo Boids.
- La variable "dist" se inicializará para almacenar la distancia.
- Para cada coordenada de los agentes, se calculará el cuadrado de la diferencia de sus coordenadas, que luego se sumará a "dist".
- Al final, el método retornará la raíz cuadrada de "dist", que se corresponderá con la distancia euclidiana entre los agentes.
Este método proporcionará un cálculo de la distancia entre los agentes en el algoritmo Boids que se utilizará para determinar cómo interactúan entre sí.
//—————————————————————————————————————————————————————————————————————————————— double C_AO_Boids::Distance (S_Boids_Agent &boid1, S_Boids_Agent &boid2) { double dist = 0; for (int c = 0; c < coords; c++) { dist += pow (boid1.x [c] - boid1.x [c], 2); } return sqrt (dist); } //——————————————————————————————————————————————————————————————————————————————
El método "Revision" de la clase "C_AO_Boids" se utilizará para actualizar la mejor solución en el algoritmo Boids.
- La variable "ind" se inicializará con el valor -1. Esta variable se usará para almacenar el índice del agente con la mejor solución.
- Para cada agente de la población, se comprobará si su función de aptitud "a[i].f" es superior a la mejor solución actual "fB". Si es así, "ind" se establecerá igual al índice de este agente.
- Si se ha encontrado un agente con la mejor solución (es decir, "ind" no es igual a -1), entonces la mejor solución "fB" y las mejores coordenadas "cB" se actualizarán con los valores de ese agente.
Este método garantizará la actualización de la mejor solución en el algoritmo Boids, lo cual ayudará al algoritmo a centrarse en las regiones más prometedoras del espacio de soluciones.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_Boids::Revision () { int ind = -1; for (int i = 0; i < popSize; i++) { if (a [i].f > fB) ind = i; } if (ind != -1) { fB = a [ind].f; ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY); } } //——————————————————————————————————————————————————————————————————————————————
3. Resultados de las pruebas
Me gustaría profundizar en los resultados del algoritmo Boids con diferentes conjuntos de características. La puntuación total de Boids en todas las funciones de la prueba ha sido de 2,22889, lo cual se corresponde con el 24,77% de la puntuación máxima posible. Este resultado indica que la eficacia del algoritmo es baja. Basándonos en esto, podemos concluir que el algoritmo Boids posee poco potencial para resolver problemas de optimización de varias funciones.
Boids|50.0|0.05|0.002|0.01|0.01|0.0001|0.01|0.001|0.0001|
=============================
5 Hilly's; Func runs: 100000; result: 0.43339505349362384
25 Hilly's; Func runs: 100000; result: 0.3058074706767012
500 Hilly's; Func runs: 100000; result: 0.2542539219446322
=============================
5 Forest's; Func runs: 100000; result: 0.35717696198531945
25 Forest's; Func runs: 100000; result: 0.20160005990319796
500 Forest's; Func runs: 100000; result: 0.15708435238635948
=============================
5 Megacity's; Func runs: 100000; result: 0.2784615384615384
25 Megacity's; Func runs: 100000; result: 0.14276923076923081
500 Megacity's; Func runs: 100000; result: 0.09833846153846236
=============================
All score: 2.22889 (24.77%)
El algoritmo Boids ocupa el puesto 28 en la tabla de clasificación, junto a su "compañero" de inteligencia de enjambre, el algoritmo PSO.
№ | AO | Description | Hilly | Hilly final | Forest | Forest final | Megacity (discrete) | Megacity final | Final result | % de MAX | ||||||
10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | ||||||||
1 | BGA | binary genetic algorithm | 0,99992 | 0,99484 | 0,50483 | 2,49959 | 1,00000 | 0,99975 | 0,32054 | 2,32029 | 0,90667 | 0,96400 | 0,23035 | 2,10102 | 6,921 | 76,90 |
2 | (P+O)ES | (P+O) evolution strategies | 0,99934 | 0,91895 | 0,56297 | 2,48127 | 1,00000 | 0,93522 | 0,39179 | 2,32701 | 0,83167 | 0,64433 | 0,21155 | 1,68755 | 6,496 | 72,18 |
3 | SDSm | stochastic diffusion search M | 0,93066 | 0,85445 | 0,39476 | 2,17988 | 0,99983 | 0,89244 | 0,19619 | 2,08846 | 0,72333 | 0,61100 | 0,10670 | 1,44103 | 5,709 | 63,44 |
4 | ESG | evolution of social groups | 0,99906 | 0,79654 | 0,35056 | 2,14616 | 1,00000 | 0,82863 | 0,13102 | 1,95965 | 0,82333 | 0,55300 | 0,04725 | 1,42358 | 5,529 | 61,44 |
5 | SIA | simulated isotropic annealing | 0,95784 | 0,84264 | 0,41465 | 2,21513 | 0,98239 | 0,79586 | 0,20507 | 1,98332 | 0,68667 | 0,49300 | 0,09053 | 1,27020 | 5,469 | 60,76 |
6 | DE | differential evolution | 0,95044 | 0,61674 | 0,30308 | 1,87026 | 0,95317 | 0,78896 | 0,16652 | 1,90865 | 0,78667 | 0,36033 | 0,02953 | 1,17653 | 4,955 | 55,06 |
7 | BSA | bird swarm algorithm | 0,90857 | 0,73661 | 0,25767 | 1,90285 | 0,90437 | 0,81619 | 0,16401 | 1,88457 | 0,61692 | 0,54154 | 0,10951 | 1,26797 | 5,055 | 56,17 |
8 | HS | harmony search | 0,86509 | 0,68782 | 0,32527 | 1,87818 | 0,99999 | 0,68002 | 0,09590 | 1,77592 | 0,62000 | 0,42267 | 0,05458 | 1,09725 | 4,751 | 52,79 |
9 | SSG | saplings sowing and growing | 0,77839 | 0,64925 | 0,39543 | 1,82308 | 0,85973 | 0,62467 | 0,17429 | 1,65869 | 0,64667 | 0,44133 | 0,10598 | 1,19398 | 4,676 | 51,95 |
10 | (PO)ES | (PO) evolution strategies | 0,79025 | 0,62647 | 0,42935 | 1,84606 | 0,87616 | 0,60943 | 0,19591 | 1,68151 | 0,59000 | 0,37933 | 0,11322 | 1,08255 | 4,610 | 51,22 |
11 | WOAm | whale optimization algorithm M | 0,84521 | 0,56298 | 0,26263 | 1,67081 | 0,93100 | 0,52278 | 0,16365 | 1,61743 | 0,66308 | 0,41138 | 0,11357 | 1,18803 | 4,476 | 49,74 |
12 | ACOm | ant colony optimization M | 0,88190 | 0,66127 | 0,30377 | 1,84693 | 0,85873 | 0,58680 | 0,15051 | 1,59604 | 0,59667 | 0,37333 | 0,02472 | 0,99472 | 4,438 | 49,31 |
13 | BFO-GA | bacterial foraging optimization - ga | 0,89150 | 0,55111 | 0,31529 | 1,75790 | 0,96982 | 0,39612 | 0,06305 | 1,42899 | 0,72667 | 0,27500 | 0,03525 | 1,03692 | 4,224 | 46,93 |
14 | MEC | mind evolutionary computation | 0,69533 | 0,53376 | 0,32661 | 1,55569 | 0,72464 | 0,33036 | 0,07198 | 1,12698 | 0,52500 | 0,22000 | 0,04198 | 0,78698 | 3,470 | 38,55 |
15 | IWO | invasive weed optimization | 0,72679 | 0,52256 | 0,33123 | 1,58058 | 0,70756 | 0,33955 | 0,07484 | 1,12196 | 0,42333 | 0,23067 | 0,04617 | 0,70017 | 3,403 | 37,81 |
16 | Micro-AIS | micro artificial immune system | 0,79547 | 0,51922 | 0,30861 | 1,62330 | 0,72956 | 0,36879 | 0,09398 | 1,19233 | 0,37667 | 0,15867 | 0,02802 | 0,56335 | 3,379 | 37,54 |
17 | COAm | cuckoo optimization algorithm M | 0,75820 | 0,48652 | 0,31369 | 1,55841 | 0,74054 | 0,28051 | 0,05599 | 1,07704 | 0,50500 | 0,17467 | 0,03380 | 0,71347 | 3,349 | 37,21 |
18 | SDOm | spiral dynamics optimization M | 0,74601 | 0,44623 | 0,29687 | 1,48912 | 0,70204 | 0,34678 | 0,10944 | 1,15826 | 0,42833 | 0,16767 | 0,03663 | 0,63263 | 3,280 | 36,44 |
19 | NMm | Nelder-Mead method M | 0,73807 | 0,50598 | 0,31342 | 1,55747 | 0,63674 | 0,28302 | 0,08221 | 1,00197 | 0,44667 | 0,18667 | 0,04028 | 0,67362 | 3,233 | 35,92 |
20 | FAm | firefly algorithm M | 0,58634 | 0,47228 | 0,32276 | 1,38138 | 0,68467 | 0,37439 | 0,10908 | 1,16814 | 0,28667 | 0,16467 | 0,04722 | 0,49855 | 3,048 | 33,87 |
21 | GSA | gravitational search algorithm | 0,64757 | 0,49197 | 0,30062 | 1,44016 | 0,53962 | 0,36353 | 0,09945 | 1,00260 | 0,32667 | 0,12200 | 0,01917 | 0,46783 | 2,911 | 32,34 |
22 | BFO | bacterial foraging optimization | 0,61171 | 0,43270 | 0,31318 | 1,35759 | 0,54410 | 0,21511 | 0,05676 | 0,81597 | 0,42167 | 0,13800 | 0,03195 | 0,59162 | 2,765 | 30,72 |
23 | ABC | artificial bee colony | 0,63377 | 0,42402 | 0,30892 | 1,36671 | 0,55103 | 0,21874 | 0,05623 | 0,82600 | 0,34000 | 0,14200 | 0,03102 | 0,51302 | 2,706 | 30,06 |
24 | BA | bat algorithm | 0,59761 | 0,45911 | 0,35242 | 1,40915 | 0,40321 | 0,19313 | 0,07175 | 0,66810 | 0,21000 | 0,10100 | 0,03517 | 0,34617 | 2,423 | 26,93 |
25 | SA | simulated annealing | 0,55787 | 0,42177 | 0,31549 | 1,29513 | 0,34998 | 0,15259 | 0,05023 | 0,55280 | 0,31167 | 0,10033 | 0,02883 | 0,44083 | 2,289 | 25,43 |
26 | IWDm | intelligent water drops M | 0,54501 | 0,37897 | 0,30124 | 1,22522 | 0,46104 | 0,14704 | 0,04369 | 0,65177 | 0,25833 | 0,09700 | 0,02308 | 0,37842 | 2,255 | 25,06 |
27 | PSO | particle swarm optimisation | 0,59726 | 0,36923 | 0,29928 | 1,26577 | 0,37237 | 0,16324 | 0,07010 | 0,60572 | 0,25667 | 0,08000 | 0,02157 | 0,35823 | 2,230 | 24,77 |
28 | Boids | boids algorithm | 0,43340 | 0,30581 | 0,25425 | 0,99346 | 0,35718 | 0,20160 | 0,15708 | 0,71586 | 0,27846 | 0,14277 | 0,09834 | 0,51957 | 2,229 | 24,77 |
29 | MA | monkey algorithm | 0,59107 | 0,42681 | 0,31816 | 1,33604 | 0,31138 | 0,14069 | 0,06612 | 0,51819 | 0,22833 | 0,08567 | 0,02790 | 0,34190 | 2,196 | 24,40 |
30 | SFL | shuffled frog-leaping | 0,53925 | 0,35816 | 0,29809 | 1,19551 | 0,37141 | 0,11427 | 0,04051 | 0,52618 | 0,27167 | 0,08667 | 0,02402 | 0,38235 | 2,104 | 23,38 |
31 | FSS | fish school search | 0,55669 | 0,39992 | 0,31172 | 1,26833 | 0,31009 | 0,11889 | 0,04569 | 0,47467 | 0,21167 | 0,07633 | 0,02488 | 0,31288 | 2,056 | 22,84 |
32 | RND | random | 0,52033 | 0,36068 | 0,30133 | 1,18234 | 0,31335 | 0,11787 | 0,04354 | 0,47476 | 0,25333 | 0,07933 | 0,02382 | 0,35648 | 2,014 | 22,37 |
33 | GWO | grey wolf optimizer | 0,59169 | 0,36561 | 0,29595 | 1,25326 | 0,24499 | 0,09047 | 0,03612 | 0,37158 | 0,27667 | 0,08567 | 0,02170 | 0,38403 | 2,009 | 22,32 |
34 | CSS | charged system search | 0,44252 | 0,35454 | 0,35201 | 1,14907 | 0,24140 | 0,11345 | 0,06814 | 0,42299 | 0,18333 | 0,06300 | 0,02322 | 0,26955 | 1,842 | 20,46 |
35 | EM | electroMagnetism-like algorithm | 0,46250 | 0,34594 | 0,32285 | 1,13129 | 0,21245 | 0,09783 | 0,10057 | 0,41085 | 0,15667 | 0,06033 | 0,02712 | 0,24412 | 1,786 | 19,85 |
Conclusiones
A pesar de los decepcionantes resultados en las funciones de prueba, cabe destacar que en las funciones Forest y Megacity con 1 000 variables los resultados resultan bastante decentes y coinciden con los resultados de los algoritmos de la mitad superior de la tabla. Este hecho puede indicarnos que el algoritmo Boids podría resultar más eficiente cuando trabajamos con un gran número de variables. En general, estos resultados muestran que sería difícil esperar más potencial del algoritmo Boids.
Figura 1. Gradación por colores de los algoritmos según sus respectivas pruebas. Los resultados superiores o iguales a 0,99 aparecen resaltados en blanco.
Figura 2. Histograma con los resultados de las pruebas de los algoritmos (en una escala de 0 a 100, cuanto mayor, mejor),
donde 100 es el máximo resultado teórico posible; hay un script para calcular la tabla de puntuación en el archivo).
Ventajas e inconvenientes del algoritmo de comportamiento de bandada (Boids):
Ventajas:
- Modelización realista del comportamiento de bandada.
Desventajas:
- Baja convergencia.
- Alta complejidad computacional.
- Baja escalabilidad en funciones suaves como Hilly (problemas con tareas de alta dimensionalidad).
Adjunto al artículo hay un archivo con las versiones actuales de los códigos. El autor de este artículo no se responsabiliza de la exactitud absoluta de la descripción de los algoritmos canónicos, muchos de ellos han sido modificados para mejorar las capacidades de búsqueda. Las conclusiones y juicios presentados en los artículos se basan en los resultados de los experimentos realizados.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/14576
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso