Volver a los desafíos
Intermedio 0 resueltos 0% de aceptación

Una generación de un algoritmo genético

Cría la siguiente generación de cadenas de bits con selección por torneo, cruce de un punto y mutación.

Lo que aprenderás

  • Seleccionar padres mediante torneos
  • Combinar soluciones con cruce
  • Añadir variación con mutación

Problema

Ver original (inglés)

Un algoritmo genético mejora una población de soluciones candidatas como lo hace la evolución: selecciona padres aptos, los combina y añade mutaciones aleatorias. Aquí los candidatos son cadenas de bits y la aptitud es el número de 1 (el problema OneMax).

Para que se pueda probar, cada elección aleatoria se proporciona. Con N cadenas (N par), construye N hijos por parejas. Para la pareja k = 0, 1, …, N/2 - 1:

  1. Selección: parent1 gana el torneo tournaments[2k] y parent2 gana tournaments[2k + 1]. Un torneo [i, j] lo gana la de population[i] y population[j] que tenga más 1; en caso de empate, gana population[i].
  2. Cruce en c = crossover_points[k]: hijo 2k = parent1[0:c] + parent2[c:] e hijo 2k + 1 = parent2[0:c] + parent1[c:].

Después aplica cada mutación [child, position] en orden invirtiendo ese bit de ese hijo.

Implementa ga_generation(population, tournaments, crossover_points, mutations).

Entrada

population: N cadenas de bits de la misma longitud L.

tournaments: N parejas de índices de la población.

crossover_points: N/2 puntos de corte, cada uno entre 1 y L - 1.

mutations: Parejas [child, position] (puede estar vacío).

Salida

Devuelve los N hijos en orden.

Restricciones

2 ≤ N ≤ 200 (N par), 2 ≤ L ≤ 64

0 ≤ número de mutaciones ≤ 1.000

Ejemplos

Ejemplo 1
Entrada:
mutations = [
    [0, 0]
]
population = ["0000", "1111", "1100", "0011"]
tournaments = [
    [0, 1],
    [2, 3],
    [1, 2],
    [0, 3]
]
crossover_points = [2, 1]

Salida esperada:
expected = ["0100", "1111", "1011", "0111"]
Entrada:
std::vector<std::vector<long long>> mutations = {
    {0, 0}
};
std::vector<std::string> population = {"0000", "1111", "1100", "0011"};
std::vector<std::vector<long long>> tournaments = {
    {0, 1},
    {2, 3},
    {1, 2},
    {0, 3}
};
std::vector<long long> crossover_points = {2, 1};

Salida esperada:
std::vector<std::string> expected = {"0100", "1111", "1011", "0111"};
Entrada:
long[][] mutations = new long[][] {
    new long[] {0, 0}
};
string[] population = new string[] {"0000", "1111", "1100", "0011"};
long[][] tournaments = new long[][] {
    new long[] {0, 1},
    new long[] {2, 3},
    new long[] {1, 2},
    new long[] {0, 3}
};
long[] crossover_points = new long[] {2, 1};

Salida esperada:
string[] expected = new string[] {"0100", "1111", "1011", "0111"};

Explicación

Pareja 0: "1111" vence a "0000", y "1100" empata con "0011", así que gana la primera. Cortar tras 2 bits da "1100" y "1111". Pareja 1: padres "1111" y "0011", corte tras 1 bit: "1011" y "0111". Por último, al hijo 0 se le invierte el bit 0: "0100".

Ejemplo 2
Entrada:
mutations = []
population = ["10", "01"]
tournaments = [
    [0, 1],
    [1, 0]
]
crossover_points = [1]

Salida esperada:
expected = ["11", "00"]
Entrada:
std::vector<std::vector<long long>> mutations = {};
std::vector<std::string> population = {"10", "01"};
std::vector<std::vector<long long>> tournaments = {
    {0, 1},
    {1, 0}
};
std::vector<long long> crossover_points = {1};

Salida esperada:
std::vector<std::string> expected = {"11", "00"};
Entrada:
long[][] mutations = new long[][] {};
string[] population = new string[] {"10", "01"};
long[][] tournaments = new long[][] {
    new long[] {0, 1},
    new long[] {1, 0}
};
long[] crossover_points = new long[] {1};

Salida esperada:
string[] expected = new string[] {"11", "00"};

Explicación

Ambas cadenas tienen un 1, así que cada torneo lo gana su primer índice: parent1 = "10" y parent2 = "01". Cortar tras 1 bit da "11" y "00".

Cómo abordarlo

Inténtalo primero por tu cuenta y luego abre las pistas una a una.

Pista 1

Construye los hijos por parejas eligiendo primero dos padres en cada torneo y luego combinando sus cadenas de bits en un punto de corte.

Pista 2

La selección depende solo del fitness, con empates que siempre favorecen al primer participante de la pareja del torneo.

Pista 3

Aplica las mutaciones después de crear todos los hijos, y asegúrate de que cada cambio afecte a la cadena del hijo actual.

Complejidad esperada

O(N * L + M) tiempo, O(N * L) espacio extra

Referencias

Este reto está adaptado para CodeUtility. Sigue el enunciado y las restricciones indicadas arriba.

Aprende de otros

Soluciones de la comunidad

Aún no hay soluciones publicadas. Resuelve este reto para compartir la primera.