Torna alle sfide
Media 0 risolte 0% di accettazione

Una generazione di un algoritmo genetico

Genera la prossima generazione di stringhe di bit con selezione a torneo, crossover a un punto e mutazione.

Cosa imparerai

  • Selezionare i genitori con i tornei
  • Combinare soluzioni con il crossover
  • Aggiungere varietà con la mutazione

Problema

Un algoritmo genetico migliora una popolazione di soluzioni candidate come fa l'evoluzione: seleziona genitori adatti, li combina e aggiunge mutazioni casuali. Qui i candidati sono stringhe di bit e la fitness è il numero di 1 (il problema OneMax).

Per renderlo verificabile, ogni scelta casuale viene fornita. Con N stringhe (N pari), costruisci N figli a coppie. Per la coppia k = 0, 1, …, N/2 - 1:

  1. Selezione: parent1 vince il torneo tournaments[2k] e parent2 vince tournaments[2k + 1]. Un torneo [i, j] lo vince quella tra population[i] e population[j] che ha più 1; in caso di parità vince population[i].
  2. Crossover in c = crossover_points[k]: figlio 2k = parent1[0:c] + parent2[c:] e figlio 2k + 1 = parent2[0:c] + parent1[c:].

Poi applica ogni mutazione [child, position] in ordine invertendo quel bit di quel figlio.

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

Input

population: N stringhe di bit della stessa lunghezza L.

tournaments: N coppie di indici della popolazione.

crossover_points: N/2 punti di taglio, ciascuno tra 1 e L - 1.

mutations: Coppie [child, position] (può essere vuoto).

Output

Restituisci gli N figli in ordine.

Vincoli

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

0 ≤ numero di mutazioni ≤ 1.000

Esempi

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

Output atteso:
expected = ["0100", "1111", "1011", "0111"]
Input:
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};

Output atteso:
std::vector<std::string> expected = {"0100", "1111", "1011", "0111"};
Input:
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};

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

Spiegazione

Coppia 0: "1111" batte "0000", e "1100" pareggia con "0011", quindi vince la prima. Tagliare dopo 2 bit dà "1100" e "1111". Coppia 1: genitori "1111" e "0011", taglio dopo 1 bit: "1011" e "0111". Infine al figlio 0 viene invertito il bit 0: "0100".

Esempio 2
Input:
mutations = []
population = ["10", "01"]
tournaments = [
    [0, 1],
    [1, 0]
]
crossover_points = [1]

Output atteso:
expected = ["11", "00"]
Input:
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};

Output atteso:
std::vector<std::string> expected = {"11", "00"};
Input:
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};

Output atteso:
string[] expected = new string[] {"11", "00"};

Spiegazione

Entrambe le stringhe hanno un solo 1, quindi ogni torneo lo vince il primo indice: parent1 = "10" e parent2 = "01". Tagliare dopo 1 bit dà "11" e "00".

Come affrontarlo

Prova prima da solo, poi apri un suggerimento alla volta.

Suggerimento 1

Costruisci i figli a coppie scegliendo prima due genitori da ogni torneo, poi combinando le loro stringhe di bit in un punto di taglio.

Suggerimento 2

La selezione dipende solo dal fitness, con i pareggi che favoriscono sempre il primo concorrente nella coppia del torneo.

Suggerimento 3

Applica le mutazioni dopo aver creato tutti i figli, e assicurati che ogni flip colpisca la stringa del figlio corrente.

Complessità attesa

tempo O(N * L + M), spazio aggiuntivo O(N * L)

Riferimenti

Questa sfida è adattata per CodeUtility. Segui la traccia e i vincoli sopra.

Impara dagli altri

Soluzioni della community

Nessuna soluzione pubblicata. Risolvi questa sfida per condividere la prima.