Algoritmo de Aprendizaje Competitivo — Competitive Learning Algorithm (CLA)
Contenido
Introducción
En las últimas décadas, se han propuesto muchos algoritmos bioinspirados, desde colonias de hormigas y enjambres de partículas hasta lobos grises y ballenas. Sin embargo, la sociedad humana, con sus complejas interacciones sociales, también puede servir como una rica fuente de ideas para crear métodos de optimización eficaces. Esta idea es la base del Algoritmo de Aprendizaje Competitivo (CLA).
El CLA utiliza una metáfora del proceso educativo, donde la población de soluciones está representada por estudiantes organizados en clases. El algoritmo modela de forma elegante tres tipos de aprendizaje: el aprendizaje del mejor alumno de la clase (el profesor), el aprendizaje a partir de la experiencia personal y el aprendizaje mediante la interacción entre clases. Este enfoque ofrece un equilibrio entre la exploración del espacio de búsqueda y el aprovechamiento de las buenas soluciones encontradas, lo cual resulta fundamental para una optimización eficaz.
En este artículo, analizaremos con detalle los principios del CLA, su base matemática y sus características de implementación, y compararemos su eficacia con otras metaheurísticas populares en nuestras funciones de prueba estándar.
Implementación del algoritmo
El CLA se basa en una metáfora del proceso educativo escolar. En esta metáfora, los estudiantes representan posibles soluciones al problema de optimización, las clases son grupos de estudiantes, los profesores son los mejores estudiantes de cada clase, el conocimiento corresponde a las coordenadas en el espacio de búsqueda y la evaluación se define por el valor de la función de aptitud.
El algoritmo comienza con una inicialización, que podemos comparar con el inicio del año escolar. Luego se crea una población de, por ejemplo, 198 estudiantes, que se dividen en 9 clases de 22 estudiantes cada una. A cada estudiante se le asigna aleatoriamente un "conocimiento" inicial: las coordenadas en el espacio de búsqueda.
El proceso de aprendizaje es iterativo y, con cada iteración, los estudiantes aprenden de tres maneras diferentes. El primer método es el aprendizaje dirigido por el profesor, donde cada estudiante aprende del mejor de su clase, y cuanto peor sea el rendimiento de la clase, más activo será el proceso de aprendizaje, pero con el tiempo la intensidad del aprendizaje disminuye para todos. El segundo método es el aprendizaje personal, que comienza solo después de la cuarta iteración. En este modo, el estudiante recuerda sus mejores resultados de las últimas cuatro "lecciones" e intenta volver a alcanzar su mejor marca personal. El tercer método consiste en aprender de otras clases, lo que también comienza después de la cuarta iteración. Existe un 50% de probabilidad de que un estudiante pueda aprender de un "profesor promedio" en todas las clases, lo cual ayuda a evitar quedarse estancado en óptimos locales.
Tras cada ciclo de entrenamiento, se evalúa el progreso. Después se identifica al mejor estudiante de cada clase, que se convierte en el profesor; se calcula la calificación general de la clase considerando el rendimiento de todos los estudiantes; y se actualiza la mejor solución encontrada a nivel global.
El algoritmo usa cinco parámetros clave:
- popSize — define el número total de estudiantes,
- numClasses — especifica el número de clases,
- beta — regula la influencia de todos los estudiantes de una clase en su calificación general,
- gamma — umbral que determina el aprendizaje a partir de otras clases,
- deltaIter — especifica después de qué iteración comienza el aprendizaje extendido utilizando la experiencia personal y las interacciones entre clases.
El algoritmo implementa varios mecanismos inteligentes. Los factores de aprendizaje adaptativo ofrecen un aprendizaje más intensivo para las clases con menor rendimiento y una desaceleración gradual del aprendizaje con el tiempo, tal como ocurre en el proceso educativo real. El equilibrio entre exploración y explotación se logra mediante la exploración activa en las etapas iniciales, seguida de un enfoque en las mejores soluciones encontradas. El mecanismo de memoria permite que el algoritmo recuerde la historia de cada estudiante y, si es necesario, vuelva a soluciones buenas encontradas anteriormente.
Matemáticamente, la actualización del conocimiento de un estudiante puede representarse como la suma de varios componentes: la lección del profesor de la clase, la experiencia personal de la historia y el conocimiento recibido del profesor promedio de todas las clases. Esta fórmula ofrece un equilibrio entre seguir al líder, utilizar la experiencia personal y explorar nuevas áreas.
En definitiva, la eficacia del algoritmo está determinada por varios factores. La diversidad se logra usando múltiples clases que exploran diferentes direcciones en el espacio de búsqueda. La competencia entre los estudiantes por un puesto de profesor estimula la mejora de las soluciones. La cooperación mediante el intercambio de conocimientos entre clases busca prevenir la convergencia prematura. La adaptabilidad ofrece ayuda adicional para las clases débiles, mientras que el mecanismo de memoria almacena información sobre las buenas soluciones.

Figura 1. Esquema del funcionamiento del algoritmo
La ilustración del funcionamiento del algoritmo describe tres pasos principales: la inicialización, la fase de entrenamiento y la evaluación con actualización. Ahora vamos a escribir el pseudocódigo para el algoritmo CLA_L.
LO QUE NECESITAMOS CON ANTELACIÓN:
- 198 estudiantes (nuestras soluciones)
- 9 clases (grupos para estudiantes)
- Parámetro β = 0,9 (¿cuán importantes son todos los estudiantes en la clase?)
- Parámetro γ = 0,5 (probabilidad de aprender de otras clases)
- Después de la cuarta iteración, incluimos los tipos de entrenamiento adicionales.
- La función para evaluar la calidad de la solución
COMENCEMOS:
1. CREANDO UNA ESCUELA:
- Distribuimos 198 estudiantes en 9 clases (22 en cada una).
- A cada estudiante le asignamos una posición inicial aleatoria en el espacio.
- En cada clase elegimos al mejor estudiante; este se convierte en profesor.
- Creamos un registro para anotar la historia de cada estudiante.
2. PROCESO DE APRENDIZAJE (proceso iterativo):
Paso 1: Determinamos la intensidad del entrenamiento
- Para cada clase calculamos con qué intensidad necesitan trabajar los estudiantes
- Las clases débiles (es decir, con mala clasificación) estudian con más intensidad.
- Con el tiempo, todos estudian con menos intensidad (como en la vida real).
Segundo paso: Encontramos el "profesor promedio"
- Tomamos las posiciones de todos los profesores
- Calculamos su valor promedio
- Este será el estándar de conocimiento para toda la escuela.
Paso 3: Cada estudiante aprende:
Por cada estudiante hacemos lo siguiente:
a) SIEMPRE aprendemos de nuestro profesor:
- Vemos dónde está nuestro profesor.
- Nos acercamos a él.
- La velocidad del movimiento depende del factor de entrenamiento de la clase.
b) Después de la cuarta iteración, recordamos nuestra experiencia:
- Analizamos el diario: ¿en qué posición obtuve mi mejor resultado durante las últimas 4 lecciones?
- Con una cierta fuerza aleatoria volvemos a esa posición.
- Esto nos ayudará a no olvidar las buenas soluciones.
c) Después de la cuarta iteración, aprendemos de otras clases:
- Lanzamos una moneda (con un 50% de probabilidad)
- Si sale cruz, miramos al "profesor promedio".
- Nos movemos ligeramente en su dirección
- Esto nos ayuda a intercambiar experiencias entre clases.
Paso 4: Evaluamos a todos los estudiantes.
- Cada estudiante recibe una calificación de aptitud (fitness).
- Cuanto mejor sea la posición, mayor será la puntuación.
Paso 5: Actualizamos la jerarquía escolar
Para cada clase:
- Encontramos un nuevo estudiante destacado: se convierte en profesor.
- Calculamos el nivel general de la clase:
* Tomamos en cuenta la evaluación del profesor.
* Sumamos la puntuación media de todos los estudiantes (multiplicada por β)
- Determinamos la puntuación total de la clase:
* Las mejores clases obtienen una clasificación baja (1, 2, 3...).
* Las peores clases obtienen la puntuación total alta
Paso 6: Recordamos la mejor solución
- Si algún profesor es mejor que nuestro récord
- Lo guardaremos como un nuevo récord.
Paso 7: Guardamos la historia.
- Mantenemos las posiciones actuales de todos los estudiantes.
- Esto será necesario para el aprendizaje personal.
3. FINALIZAMOS:
- Retornamos la mejor solución encontrada.
- Y su valoración.
Ahora comenzaremos a escribir el código del algoritmo. La clase "C_AO_CLA_l" hereda de la clase "C_AO" (clase padre) e implementa un algoritmo basado en el concepto de aprendizaje competitivo. La clase contiene una serie de parámetros configurables por el usuario:
- popSize — tamaño de la población (número de "agentes" o "estudiantes");
- numClasses — número de clases en las que se dividen los agentes;
- beta — parámetro que probablemente influye en la velocidad de aprendizaje o adaptación;
- gamma — otro parámetro que probablemente esté relacionado con el aprendizaje;
- deltaIter — frecuencia de ejecución de ciertos pasos del algoritmo (en iteraciones).
El constructor inicializa los parámetros principales, establece el nombre y la descripción del algoritmo, especifica los valores predeterminados para las variables y dimensiona el array "params", que está destinado a almacenar información sobre los parámetros del algoritmo.
Métodos:
- SetParams () — actualiza los valores de las variables internas de la clase (parámetros del algoritmo) utilizando los valores del array "params". Esto permite cambiar parámetros configurados externamente.
- Init () — inicializa el algoritmo. Acepta parámetros para definir los límites y el paso para cambiar los parámetros.
- Moving () — método principal para "mover" los agentes (realizando iteraciones y pasos del algoritmo).
- Revision () — el método está asociado con la corrección o mejora de las soluciones.
- Injection () — método para insertar nuevos conocimientos o datos en un agente.
- numClasses, beta, gamma, deltaIter — parámetros del algoritmo.
- currentIter, studentsPerClass, totalIters — variables internas para controlar el desarrollo del algoritmo.
- teachers[] — array de estructuras "S_AO_Agent" que representan a los "profesores" en cada clase (puntos de referencia).
- classRanks[] — clasificaciones de clase que reflejan su rendimiento.
- classTotalCosts[] — costos totales o "costo" de cada clase.
- CL[] — array temporal para calcular el conocimiento promedio de los profesores.
- UpdateTeachersAndCosts() — método interno para actualizar la información sobre los profesores y sus costos.
- UpdateClassRanks() — método interno para actualizar las clasificaciones de las clases.
- UpdateStudentsKnowledge() — método interno para actualizar los parámetros de conocimiento o "estudiantes".
//———————————————————————————————————————————————————————————————————— class C_AO_CLA_l : public C_AO { public: //---------------------------------------------------------- ~C_AO_CLA_l () { } C_AO_CLA_l () { ao_name = "CLA_L"; ao_desc = "Competitive Learning Algorithm"; ao_link = "https://www.mql5.com/en/articles/18857"; popSize = 198; numClasses = 3; beta = 0.3; gamma = 0.8; deltaIter = 2; ArrayResize (params, 5); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "numClasses"; params [1].val = numClasses; params [2].name = "beta"; params [2].val = beta; params [3].name = "gamma"; params [3].val = gamma; params [4].name = "deltaIter"; params [4].val = deltaIter; } void SetParams () { popSize = (int)params [0].val; numClasses = (int)params [1].val; beta = params [2].val; gamma = params [3].val; deltaIter = (int)params [4].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); void Injection (const int popPos, const int coordPos, const double value) { } //------------------------------------------------------------------ int numClasses; double beta; double gamma; int deltaIter; private: //--------------------------------------------------------- int currentIter; int studentsPerClass; int totalIters; // Structures for classes S_AO_Agent teachers []; double classRanks []; double classTotalCosts []; // Temporary array double CL []; // Average knowledge of teachers // Auxiliary methods void UpdateTeachersAndCosts (); void UpdateClassRanks (); void UpdateStudentsKnowledge (); }; //————————————————————————————————————————————————————————————————————
El método "Init()" de la clase "C_AO_CLA_l" inicializa el algoritmo antes de que comience a funcionar. La lógica del trabajo se divide en varias etapas. Standard Initialization: se llama al método "StandardInit()" y se realiza la inicialización estándar para el algoritmo de optimización. Se le pasan los parámetros "rangeMinP", "rangeMaxP" y el paso "rangeStepP". Si la inicialización estándar falla, el método Init() devuelve "false".
Initialization of Algorithm Parameters: restablece el contador de iteraciones actual a "0", establece el número total de iteraciones que el algoritmo debe realizar utilizando el valor pasado en el argumento "epochsP", calcula el número de "estudiantes" (agentes) en cada clase dividiendo el tamaño de la población por el número de clases. A continuación, el método comprueba que haya al menos un estudiante en cada clase. Luego el ciclo "for" inicializa a cada agente de la población. Para cada agente en el array "a", se llama al método "Init()". El método consiste en inicializar el número de coordenadas para cada agente.
El ciclo "for" inicializa un "profesor" para cada clase, establece la clasificación de cada clase en "1.0" y establece el "costo" inicial de cada clase en un valor máximo negativo para garantizar que se encuentre una mejor solución más adelante. Si todos los pasos de inicialización se realizan correctamente, el método devuelve "true".
//———————————————————————————————————————————————————————————————————— bool C_AO_CLA_l::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ currentIter = 0; totalIters = epochsP; studentsPerClass = popSize / numClasses; if (studentsPerClass < 1) { Print ("Error: Too few students per class"); return false; } // Adjust the population size //spopSize = studentsPerClass * numClasses; ArrayResize (a, popSize); for (int i = 0; i < popSize; i++) a [i].Init (coords); // Initialize class structures ArrayResize (teachers, numClasses); ArrayResize (classRanks, numClasses); ArrayResize (classTotalCosts, numClasses); for (int i = 0; i < numClasses; i++) { teachers [i].Init (coords); classRanks [i] = 1.0; classTotalCosts [i] = -DBL_MAX; } // Temporary array ArrayResize (CL, coords); return true; } //————————————————————————————————————————————————————————————————————
El método "Moving()" de la clase "C_AO_CLA_l" implementa una iteración del algoritmo de optimización principal, comprueba si es la primera iteración y, si lo es (es decir, "revision" es false), el método inicializa las posiciones de todos los agentes de la población con valores aleatorios en el rango dado. Itera sobre todos los agentes de la población y, para cada agente, itera sobre todas sus coordenadas.
Para cada coordenada, el método genera un valor aleatorio "val" en el rango de "rangeMin[c]" a "rangeMax[c]" usando el método "u.RNDfromCI()", que proporciona una función para generar números aleatorios. Este asigna el valor "val" a la coordenada "a[i].c[c]" del agente "i", habiéndola "recortado" previamente para que se encuentre dentro del rango aceptable y corresponda al paso dado "rangeStep[c]". Luego se utiliza el método "u.SeInDiSp()", que realiza la comprobación y el ajuste de valores.
Tras inicializar la población, se establece una bandera para señalar que la inicialización se ha realizado correctamente. Si no se trata de la primera iteración, aumenta el contador de iteraciones actual. Llama al método "UpdateStudentsKnowledge()", que implementa el mecanismo principal para el aprendizaje o la adaptación de los agentes según los principios de aprendizaje competitivo usados en el algoritmo "CLA_L". La lógica de este método determina cómo interactúan los agentes, intercambian información y mejoran sus posiciones en el espacio de búsqueda de soluciones.
//———————————————————————————————————————————————————————————————————— void C_AO_CLA_l::Moving () { // Initial population setup if (!revision) { for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { double val = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [i].c [c] = u.SeInDiSp (val, rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } currentIter++; // Update students' knowledge UpdateStudentsKnowledge (); } //————————————————————————————————————————————————————————————————————
El método "UpdateStudentsKnowledge()" se encarga de actualizar la posición de cada estudiante (agente) durante el proceso de optimización. Luego se calcula el progreso actual de la optimización. Y se calculan dos factores clave de aprendizaje:
- TF (Teaching Factor) — muestra cuánto aprende un estudiante de un profesor.
- CF (Confirmatory Factor) — muestra hasta qué punto un estudiante tiene en cuenta el conocimiento promedio de los profesores. Estos factores dependen del progreso de la optimización y de la puntuación total de la clase del estudiante.
A continuación, se calcula el valor medio de las coordenadas de todos los profesores “activos” (profesores cuyos resultados son válidos). Este conocimiento promedio se almacena en un array temporal. Para cada estudiante (agente), se determina la clase a la que pertenece, y para cada coordenada del estudiante, se calcula una nueva posición del estudiante sumando la coordenada actual con las "fuentes de conocimiento". El estudiante aprende de su profesor ajustando su posición en una cantidad proporcional a la diferencia entre la coordenada del profesor y la suya propia, multiplicada por TF. Si han transcurrido suficientes iteraciones (currentIter > deltaIter) y el estudiante tiene una "mejor solución", entonces aprende de su experiencia previa exitosa ajustando su posición hacia la mejor coordenada, multiplicada por un factor aleatorio.
Si han transcurrido suficientes iteraciones y el número de profesores es válido, el estudiante aprende del conocimiento promedio de todos los profesores, ajustando su posición en la dirección de la coordenada promedio multiplicada por "CF" con una cierta probabilidad (1-gamma). Luego se aplican límites (restricciones) a cada coordenada del estudiante para garantizar que el valor se encuentre dentro del rango aceptable. El valor se ajusta para que coincida con los valores aceptables dado el paso de discretización del espacio de búsqueda.
//———————————————————————————————————————————————————————————————————— void C_AO_CLA_l::UpdateStudentsKnowledge () { // Calculate learning factors for the current iteration double progress = (double)currentIter / (double)MathMax (totalIters, 100); // Calculate the average knowledge of all teachers (CL) ArrayInitialize (CL, 0.0); int validTeachers = 0; for (int k = 0; k < numClasses; k++) { // Make sure the teacher is initialized if (teachers [k].f > -DBL_MAX) { for (int c = 0; c < coords; c++) { CL [c] += teachers [k].c [c]; } validTeachers++; } } if (validTeachers > 0) { for (int c = 0; c < coords; c++) { CL [c] /= validTeachers; } } // Update each student for (int i = 0; i < popSize; i++) { int classIdx = i / studentsPerClass; // Teaching Factor - decreases with progress double TF = MathExp (-0.6 * progress * classRanks [classIdx]); TF = MathMax (0.1, MathMin (1.0, TF)); // Confirmatory Factor double CF = MathExp (-0.5 * progress * classRanks [classIdx]); CF = MathMax (0.1, MathMin (1.0, CF)); // Update the student position for (int c = 0; c < coords; c++) { double newPos = a [i].c [c]; // a) Teacher Learning - learn from the class teacher if (teachers [classIdx].f > -DBL_MAX) { newPos += TF * (teachers [classIdx].c [c] - a [i].c [c]); } // b) Personal Learning - learn from your best solution if (currentIter > deltaIter && a [i].fB > -DBL_MAX) { double PF = u.RNDprobab (); newPos += PF * (a [i].cB [c] - a [i].c [c]); } // c) Confirmatory Learning - learn from the average of all teachers if (currentIter > deltaIter && validTeachers > 0) { double rnd = u.RNDprobab (); if (rnd >= gamma) // Participate with probability (1-gamma) { newPos += CF * (CL [c] - a [i].c [c]); } } // Apply borders a [i].c [c] = u.SeInDiSp (newPos, rangeMin [c], rangeMax [c], rangeStep [c]); } } } //————————————————————————————————————————————————————————————————————
El método "Revision()" de la clase "C_AO_CLA_l" se usa para actualizar la información sobre las mejores soluciones de cada agente, así como para actualizar la mejor solución global y los parámetros correspondientes.
Para cada agente, se comprueba si su función objetivo actual (a[i].f) ha mejorado en comparación con su mejor valor recordado (a[i].fB). En ese caso, se guarda el mejor valor y se copian las coordenadas correspondientes. Tras comprobar todos los agentes, si alguno de ellos obtiene un valor de función mejor que el máximo global actual (fB), se actualizan los parámetros globales: fB es el mejor valor de función global, mientras que cB son las coordenadas de esta mejor solución.
A continuación, se llama al método "UpdateTeachersAndCosts()", que actualiza la información sobre los mejores profesores. Al final, se llama a "UpdateClassRanks()" para recalcular las clasificaciones de las clases, lo cual influye en la prioridad de elección de ciertas soluciones en iteraciones posteriores.
//———————————————————————————————————————————————————————————————————— void C_AO_CLA_l::Revision () { for (int i = 0; i < popSize; i++) { // Update personal best positions if (a [i].f > a [i].fB) { a [i].fB = a [i].f; ArrayCopy (a [i].cB, a [i].c); } // Update the global best one if (a [i].f > fB) { fB = a [i].f; ArrayCopy (cB, a [i].c); } } // Update teachers UpdateTeachersAndCosts (); // Update class ranks UpdateClassRanks (); } //————————————————————————————————————————————————————————————————————
El método "UpdateTeachersAndCosts()" se usa para actualizar la información sobre los profesores de cada clase y calcular el "costo" total de la clase. El método itera todas las clases definidas en el sistema. Para cada clase, se determinan los índices inicial y final de los estudiantes (agentes) incluidos en dicha clase.
Para encontrar al mejor estudiante de la clase, se inicializan variables para almacenar el mejor valor de la función, el índice del mejor estudiante, la suma de los valores de la función de todos los estudiantes de la clase y el número de estudiantes válidos. El ciclo se repite con todos los estudiantes de la clase actual. Para cada estudiante, se comprueba si su solución es válida y, si lo es, entonces: el valor de la función de este estudiante se suma a "sumFitness", se incrementa el contador de estudiantes válidos y, si el valor de la función del estudiante actual es mejor que el mejor valor actual "bestFitness", entonces se actualiza y se actualiza "bestIdx".
Después de encontrar al mejor estudiante de la clase, se comprueba que haya habido al menos un estudiante válido en la clase (validStudents > 0). Si se cumple la condición, las coordenadas del mejor estudiante se copian a las coordenadas del profesor de la clase correspondiente, mientras que el valor de la función del mejor estudiante se asigna al valor de la función del profesor.
Luego se calcula el valor promedio de la función de todos los estudiantes de la clase "avgFitness". El "costo" total de la clase se calcula como la suma del valor de la función del mejor estudiante y el producto del coeficiente "beta" por el valor medio de la función de todos los estudiantes. Es una combinación de la eficacia del profesor y el rendimiento medio de la clase.
//———————————————————————————————————————————————————————————————————— void C_AO_CLA::UpdateTeachersAndCosts () { for (int k = 0; k < numClasses; k++) { int startIdx = k * studentsPerClass; int endIdx = startIdx + studentsPerClass; double bestFitness = -DBL_MAX; int bestIdx = startIdx; double sumFitness = 0.0; int validStudents = 0; // Find the best student (teacher) in the class for (int i = startIdx; i < endIdx; i++) { if (a[i].f > -DBL_MAX) // Validity check { sumFitness += a[i].f; validStudents++; if (a[i].f > bestFitness) { bestFitness = a[i].f; bestIdx = i; } } } // Update the teacher if (validStudents > 0) { ArrayCopy (teachers[k].c, a[bestIdx].c); teachers[k].f = bestFitness; // Calculate the class total cost double avgFitness = sumFitness / validStudents; classTotalCosts[k] = bestFitness + beta * avgFitness; } } } //————————————————————————————————————————————————————————————————————
El método "UpdateClassRanks()" se usa para determinar la clasificación de las clases en función de su "costo" general, que caracteriza la eficacia de la clase. El proceso del método comienza por encontrar el costo mínimo y máximo entre todas las clases válidas. Si todas las clases tienen el mismo valor, o no hay clases válidas, entonces se establece el mismo rango de 1 para todas.
Si existen diferencias de costo, estos valores se normalizan: cada costo se ajusta a un rango de 0 a 1. Después de esto, se realiza una inversión: las clases con un costo mayor reciben una clasificación inferior, cercana a 1, y las que tienen un costo menor reciben una clasificación máxima igual al número de clases.
A continuación, cada valor de clasificación se limita a un valor mínimo y máximo para evitar valores fuera de los límites establecidos. En definitiva, basándose en los resultados de este método, se asignan clasificaciones a las clases según su rendimiento.
//———————————————————————————————————————————————————————————————————— void C_AO_CLA_l::UpdateClassRanks () { // Find the min and max costs among valid classes double minCost = DBL_MAX; double maxCost = -DBL_MAX; int validClasses = 0; for (int k = 0; k < numClasses; k++) { if (classTotalCosts [k] > -DBL_MAX) { if (classTotalCosts [k] < minCost) minCost = classTotalCosts [k]; if (classTotalCosts [k] > maxCost) maxCost = classTotalCosts [k]; validClasses++; } } if (validClasses == 0 || maxCost - minCost < 1e-10) { // All classes have the same score for (int k = 0; k < numClasses; k++) classRanks [k] = 1.0; } else { // Ranking: best classes (high cost) get low rank for (int k = 0; k < numClasses; k++) { if (classTotalCosts [k] > -DBL_MAX) { // Normalize from 0 to 1 double normalized = (classTotalCosts [k] - minCost) / (maxCost - minCost); // Inversion: the best get a rank close to 1 classRanks [k] = 1.0 + (1.0 - normalized) * (numClasses - 1.0); // Limitation classRanks [k] = MathMax (1.0, MathMin ((double)numClasses, classRanks [k])); } else { classRanks [k] = numClasses; // Worst rank for uninitialized } } } } //————————————————————————————————————————————————————————————————————
Resultados de las pruebas
Según los resultados de las pruebas, el algoritmo CLA_L ha obtenido resultados decentes.CLA_L|Competitive Learning Algorithm|198.0|3.0|0.3|0.8|2.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.6482993681242128
25 Hilly's; Func runs: 10000; result: 0.5535249826770444
500 Hilly's; Func runs: 10000; result: 0.2584959913710746
=============================
5 Forest's; Func runs: 10000; result: 0.8027208362980616
25 Forest's; Func runs: 10000; result: 0.49540442971179494
500 Forest's; Func runs: 10000; result: 0.20048686632188017
=============================
5 Megacity's; Func runs: 10000; result: 0.6353846153846153
25 Megacity's; Func runs: 10000; result: 0.2716923076923076
500 Megacity's; Func runs: 10000; result: 0.10086153846153936
=============================
All score: 3.96687 (44.08%)
La visualización muestra que el algoritmo inicialmente tiene buenas capacidades de búsqueda, las cuales se deterioran al quedar atrapado en óptimos locales, lo que lleva a un estancamiento en la segunda mitad de la ejecución del algoritmo.

CLA_L en la función de prueba Hilly

CLA_L en la función de prueba Forest

CLA_L en la función de prueba Megacity
Basándonos en los resultados del algoritmo, le presentamos la tabla comparativa; CLA_L se muestra con fines informativos.
| № | AO | Description | Hilly | Hilly Final | Forest | Forest Final | Megacity (discrete) | Megacity Final | Final Result | % of 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 | ANS | across neighbourhood search | 0,94948 | 0,84776 | 0,43857 | 2,23581 | 1,00000 | 0,92334 | 0,39988 | 2,32323 | 0,70923 | 0,63477 | 0,23091 | 1,57491 | 6,134 | 68,15 |
| 2 | CLA | code lock algorithm (joo) | 0,95345 | 0,87107 | 0,37590 | 2,20042 | 0,98942 | 0,91709 | 0,31642 | 2,22294 | 0,79692 | 0,69385 | 0,19303 | 1,68380 | 6,107 | 67,86 |
| 3 | AMOm | animal migration optimization M | 0,90358 | 0,84317 | 0,46284 | 2,20959 | 0,99001 | 0,92436 | 0,46598 | 2,38034 | 0,56769 | 0,59132 | 0,23773 | 1,39675 | 5,987 | 66,52 |
| 4 | (P+O)ES | (P+O) evolution strategies | 0,92256 | 0,88101 | 0,40021 | 2,20379 | 0,97750 | 0,87490 | 0,31945 | 2,17185 | 0,67385 | 0,62985 | 0,18634 | 1,49003 | 5,866 | 65,17 |
| 5 | CTA | comet tail algorithm (joo) | 0,95346 | 0,86319 | 0,27770 | 2,09435 | 0,99794 | 0,85740 | 0,33949 | 2,19484 | 0,88769 | 0,56431 | 0,10512 | 1,55712 | 5,846 | 64,96 |
| 6 | TETA | time evolution travel algorithm (joo) | 0,91362 | 0,82349 | 0,31990 | 2,05701 | 0,97096 | 0,89532 | 0,29324 | 2,15952 | 0,73462 | 0,68569 | 0,16021 | 1,58052 | 5,797 | 64,41 |
| 7 | 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 |
| 8 | BOAm | billiards optimization algorithm M | 0,95757 | 0,82599 | 0,25235 | 2,03590 | 1,00000 | 0,90036 | 0,30502 | 2,20538 | 0,73538 | 0,52523 | 0,09563 | 1,35625 | 5,598 | 62,19 |
| 9 | AAm | archery algorithm M | 0,91744 | 0,70876 | 0,42160 | 2,04780 | 0,92527 | 0,75802 | 0,35328 | 2,03657 | 0,67385 | 0,55200 | 0,23738 | 1,46323 | 5,548 | 61,64 |
| 10 | ESG | evolution of social groups (joo) | 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 |
| 11 | SIA | simulated isotropic annealing (joo) | 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 |
| 12 | EOm | extremal_optimization_M | 0,76166 | 0,77242 | 0,31747 | 1,85155 | 0,99999 | 0,76751 | 0,23527 | 2,00277 | 0,74769 | 0,53969 | 0,14249 | 1,42987 | 5,284 | 58,71 |
| 13 | BBO | biogeography based optimization | 0,94912 | 0,69456 | 0,35031 | 1,99399 | 0,93820 | 0,67365 | 0,25682 | 1,86867 | 0,74615 | 0,48277 | 0,17369 | 1,40261 | 5.265 | 58,50 |
| 14 | ACS | artificial cooperative search | 0,75547 | 0,74744 | 0,30407 | 1,80698 | 1,00000 | 0,88861 | 0,22413 | 2,11274 | 0,69077 | 0,48185 | 0,13322 | 1,30583 | 5,226 | 58,06 |
| 15 | DA | dialectical algorithm | 0,86183 | 0,70033 | 0,33724 | 1,89940 | 0,98163 | 0,72772 | 0,28718 | 1,99653 | 0,70308 | 0,45292 | 0,16367 | 1,31967 | 5,216 | 57,95 |
| 16 | BHAm | black hole algorithm M | 0,75236 | 0,76675 | 0,34583 | 1,86493 | 0,93593 | 0,80152 | 0,27177 | 2,00923 | 0,65077 | 0,51646 | 0,15472 | 1,32195 | 5.196 | 57.73 |
| 17 | ASO | anarchy society optimization | 0,84872 | 0,74646 | 0,31465 | 1,90983 | 0,96148 | 0,79150 | 0,23803 | 1,99101 | 0,57077 | 0,54062 | 0,16614 | 1,27752 | 5,178 | 57,54 |
| 18 | RFO | royal flush optimization (joo) | 0,83361 | 0,73742 | 0,34629 | 1,91733 | 0,89424 | 0,73824 | 0,24098 | 1,87346 | 0,63154 | 0,50292 | 0,16421 | 1,29867 | 5,089 | 56,55 |
| 19 | AOSm | búsqueda de orbitales atómicos M | 0,80232 | 0,70449 | 0,31021 | 1,81702 | 0,85660 | 0,69451 | 0,21996 | 1,77107 | 0,74615 | 0,52862 | 0,14358 | 1,41835 | 5,006 | 55,63 |
| 20 | TSEA | turtle shell evolution algorithm (joo) | 0,96798 | 0,64480 | 0,29672 | 1,90949 | 0,99449 | 0,61981 | 0,22708 | 1,84139 | 0,69077 | 0,42646 | 0,13598 | 1,25322 | 5,004 | 55,60 |
| 21 | BSA | backtracking_search_algorithm | 0,97309 | 0,54534 | 0,29098 | 1,80941 | 0,99999 | 0,58543 | 0,21747 | 1,80289 | 0,84769 | 0,36953 | 0,12978 | 1,34700 | 4.959 | 55.10 |
| 22 | 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 |
| 23 | SRA | successful restaurateur algorithm (joo) | 0,96883 | 0,63455 | 0,29217 | 1,89555 | 0,94637 | 0,55506 | 0,19124 | 1,69267 | 0,74923 | 0,44031 | 0,12526 | 1,31480 | 4,903 | 54,48 |
| 24 | CRO | chemical reaction optimisation | 0,94629 | 0,66112 | 0,29853 | 1,90593 | 0,87906 | 0,58422 | 0,21146 | 1,67473 | 0,75846 | 0,42646 | 0,12686 | 1,31178 | 4,892 | 54,36 |
| 25 | BIO | blood inheritance optimization (joo) | 0,81568 | 0,65336 | 0,30877 | 1,77781 | 0,89937 | 0,65319 | 0,21760 | 1,77016 | 0,67846 | 0,47631 | 0,13902 | 1,29378 | 4,842 | 53,80 |
| 26 | BSA | bird swarm algorithm | 0,89306 | 0,64900 | 0,26250 | 1,80455 | 0,92420 | 0,71121 | 0,24939 | 1,88479 | 0,69385 | 0,32615 | 0,10012 | 1,12012 | 4,809 | 53,44 |
| 27 | DEA | dolphin_echolocation_algorithm | 0,75995 | 0,67572 | 0,34171 | 1,77738 | 0,89582 | 0,64223 | 0,23941 | 1,77746 | 0,61538 | 0,44031 | 0,15115 | 1,20684 | 4.762 | 52,91 |
| 28 | 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 |
| 29 | 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 |
| 30 | BCOm | bacterial chemotaxis optimization M | 0,75953 | 0,62268 | 0,31483 | 1,69704 | 0,89378 | 0,61339 | 0,22542 | 1,73259 | 0,65385 | 0,42092 | 0,14435 | 1,21912 | 4,649 | 51,65 |
| 31 | ABO | african buffalo optimization | 0,83337 | 0,62247 | 0,29964 | 1,75548 | 0,92170 | 0,58618 | 0,19723 | 1,70511 | 0,61000 | 0,43154 | 0,13225 | 1,17378 | 4,634 | 51,49 |
| 32 | (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 |
| 33 | FBA | fractal-based Algorithm | 0,79000 | 0,65134 | 0,28965 | 1,73099 | 0,87158 | 0,56823 | 0,18877 | 1,62858 | 0,61077 | 0,46062 | 0,12398 | 1,19537 | 4.555 | 50,61 |
| 34 | TSm | tabu search M | 0,87795 | 0,61431 | 0,29104 | 1,78330 | 0,92885 | 0,51844 | 0,19054 | 1,63783 | 0,61077 | 0,38215 | 0,12157 | 1,11449 | 4,536 | 50,40 |
| 35 | BSO | brain storm optimization | 0,93736 | 0,57616 | 0,29688 | 1,81041 | 0,93131 | 0,55866 | 0,23537 | 1,72534 | 0,55231 | 0,29077 | 0,11914 | 0,96222 | 4,498 | 49,98 |
| 36 | WOAm | wale 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 |
| 37 | AEFA | artificial electric field algorithm | 0,87700 | 0,61753 | 0,25235 | 1,74688 | 0,92729 | 0,72698 | 0,18064 | 1,83490 | 0,66615 | 0,11631 | 0,09508 | 0,87754 | 4,459 | 49,55 |
| 38 | AEO | artificial ecosystem-based optimization algorithm | 0,91380 | 0,46713 | 0,26470 | 1,64563 | 0,90223 | 0,43705 | 0,21400 | 1,55327 | 0,66154 | 0,30800 | 0,28563 | 1,25517 | 4,454 | 49,49 |
| 39 | CAm | camel algorithm M | 0,78684 | 0,56042 | 0,35133 | 1,69859 | 0,82772 | 0,56041 | 0,24336 | 1,63149 | 0,64846 | 0,33092 | 0,13418 | 1,11356 | 4.444 | 49.37 |
| 40 | 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 |
| 41 | CMAES | covariance_matrix_adaptation_evolution_strategy | 0,76258 | 0,72089 | 0,00000 | 1,48347 | 0,82056 | 0,79616 | 0,00000 | 1,61672 | 0,75846 | 0,49077 | 0,00000 | 1,24923 | 4.349 | 48.33 |
| 42 | 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 |
| 43 | SOA | simple optimization algorithm | 0,91520 | 0,46976 | 0,27089 | 1,65585 | 0,89675 | 0,37401 | 0,16984 | 1,44060 | 0,69538 | 0,28031 | 0,10852 | 1,08422 | 4.181 | 46,45 |
| 44 | ABHA | artificial bee hive algorithm | 0,84131 | 0,54227 | 0,26304 | 1,64663 | 0,87858 | 0,47779 | 0,17181 | 1,52818 | 0,50923 | 0,33877 | 0,10397 | 0,95197 | 4.127 | 45,85 |
| 45 | ACMO | atmospheric cloud model optimization | 0,90321 | 0,48546 | 0,30403 | 1,69270 | 0,80268 | 0,37857 | 0,19178 | 1,37303 | 0,62308 | 0,24400 | 0,10795 | 0,97503 | 4,041 | 44,90 |
| CLA_L | competitivelearningalgorithm | 0,64829 | 0,55352 | 0,25849 | 1,46030 | 0,80272 | 0,49540 | 0,20048 | 1,49860 | 0,63538 | 0,27169 | 0,10086 | 1,00793 | 3,967 | 44,08 | |
| RW | random walk | 0,48754 | 0,32159 | 0,25781 | 1,06694 | 0,37554 | 0,21944 | 0,15877 | 0,75375 | 0,27969 | 0,14917 | 0,09847 | 0,52734 | 2.348 | 26,09 | |
Conclusiones
El algoritmo de aprendizaje competitivo (CLA_L) demuestra un rendimiento promedio en la resolución de problemas de optimización estándar. El análisis del funcionamiento del algoritmo ha revelado una dinámica característica de dos fases: una fase de exploración activa en las iteraciones iniciales y una fase de estancamiento prematuro en la segunda mitad del proceso de optimización.
La división de la población en clases garantiza una buena cobertura del espacio de búsqueda en las primeras etapas. Cada clase explora su propia área, lo cual contribuye a una búsqueda global. El modelo educativo hace que el algoritmo sea claro y fácil de interpretar. Los conceptos de profesores, estudiantes y diferentes tipos de aprendizaje son fáciles de comprender. En teoría, los factores de aprendizaje que dependen de la puntuación total en la clase deberían ofrecer un equilibrio entre la exploración y la explotación. La combinación de tres tipos de aprendizaje (dirigido por el profesor, personal y confirmatorio) crea el potencial para evitar óptimos locales. A pesar de los mecanismos de diversificación, el algoritmo tiende a quedarse atascado en óptimos locales.
El algoritmo CLA presenta un concepto interesante que consiste en aplicar una metáfora educativa a problemas de optimización. A pesar del enfoque innovador y el buen rendimiento inicial, el algoritmo necesita modificaciones significativas para poder competir con las metaheurísticas modernas. El principal reto sigue siendo garantizar un equilibrio entre la exploración y la explotación a lo largo de todo el proceso de optimización.

Figura 2. Clasificación por colores de los algoritmos según las pruebas respectivas

Figura 3. Histograma de los resultados de las pruebas de algoritmos (en una escala de 0 a 100, cuanto más mejor, donde 100 es el máximo resultado teórico posible, el script para calcular la tabla de puntuación está en el archivo)
Ventajas e inconvenientes del algoritmo CLA_L:
Ventajas:
- Es rápido.
Desventajas:
- Gran número de parámetros externos.
- Tendencia a quedarse atascado en las tareas.
Adjuntamos al artículo un archivo con las versiones actuales de los códigos de los algoritmos. 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.
Programas usados en el artículo
| # | Nombre | Tipo | Descripción |
|---|---|---|---|
| 1 | #C_AO.mqh | Archivo de inclusión | Clase base de algoritmos de optimización basados en la población |
| 2 | #C_AO_enum.mqh | Archivo de inclusión | Enumeración de los algoritmos de optimización basados en la población |
| 3 | TestFunctions.mqh | Archivo de inclusión | Biblioteca de funciones de prueba |
| 4 | TestStandFunctions.mqh | Archivo de inclusión | Biblioteca de funciones del banco de pruebas |
| 5 | Utilities.mqh | Archivo de inclusión | Biblioteca de funciones auxiliares |
| 6 | CalculationTestResults.mqh | Archivo de inclusión | Script para calcular los resultados en una tabla comparativa |
| 7 | Testing AOs.mq5 | Script | Banco de pruebas único para todos los algoritmos de optimización basados en la población |
| 8 | Simple use of population optimization algorithms.mq5 | Script | Ejemplo sencillo de utilización de algoritmos de optimización basados en la población sin visualización |
| 9 | Test_AO_CLA_L.mq5 | Script | Banco de pruebas para CLA_L |
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/18857
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Utilizando redes neuronales en MetaTrader
De novato a experto: Revelando las sombras de las velas japonesas (mechas)
Particularidades del trabajo con números del tipo double en MQL4
Redes neuronales en el trading: Modelo multidimensional de extremo a extremo para la previsión de series temporales (Final)
- 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