Problema del viajante de comercio utilizando un algoritmo genético

Requisitos previos: algoritmo genético , problema del viajante
de comercio En este artículo, se propone un algoritmo genético para resolver el problema del viajero de comercio . 
Los algoritmos genéticos son algoritmos de búsqueda heurística inspirados en el proceso que sustenta la evolución de la vida. El algoritmo está diseñado para replicar el proceso de selección natural para llevar a cabo la generación, es decir, la supervivencia del más apto de los seres. Los algoritmos genéticos estándar se dividen en cinco fases que son: 
 

  1. Creación de población inicial.
  2. Cálculo de la aptitud.
  3. Selección de los mejores genes.
  4. Cruzando.
  5. Mutar para introducir variaciones.

Estos algoritmos se pueden implementar para encontrar una solución a los problemas de optimización de varios tipos. Uno de esos problemas es el problema del viajante de comercio . El problema dice que a un vendedor se le da un conjunto de ciudades, tiene que encontrar la ruta más corta para visitar cada ciudad exactamente una vez y regresar a la ciudad de origen. 
Enfoque: En la siguiente implementación, las ciudades se toman como genes, la string generada usando estos caracteres se llama cromosoma, mientras que una puntuación de aptitud que es igual a la longitud de la ruta de todas las ciudades mencionadas, se usa para apuntar a una población.
Fitness Score se define como la longitud del camino descrito por el gen. Cuanto menor sea el ajuste de la longitud del camino es el gen. El más apto de todos los genes del acervo genético sobrevive a la prueba de población y pasa a la siguiente iteración. El número de iteraciones depende del valor de una variable de enfriamiento. El valor de la variable de enfriamiento continúa disminuyendo con cada iteración y alcanza un umbral después de un cierto número de iteraciones.
Algoritmo: 
 

1. Initialize the population randomly.
2. Determine the fitness of the chromosome.
3. Until done repeat:
    1. Select parents.
    2. Perform crossover and mutation.
    3. Calculate the fitness of the new population.
    4. Append it to the gene pool.

pseudo-código 
 

Initialize procedure GA{
    Set cooling parameter = 0;
    Evaluate population P(t);
    While( Not Done ){
        Parents(t) = Select_Parents(P(t));
        Offspring(t) = Procreate(P(t));
        p(t+1) = Select_Survivors(P(t), Offspring(t));
        t = t + 1; 
    }
}

¿Cómo funciona la mutación?
Supongamos que hay 5 ciudades: 0, 1, 2, 3, 4. El vendedor está en la ciudad 0 y tiene que encontrar la ruta más corta para viajar a través de todas las ciudades de regreso a la ciudad 0. Un cromosoma que representa la ruta elegida puede ser representado como: 
 

Este cromosoma sufre una mutación. Durante la mutación, la posición de dos ciudades en el cromosoma se intercambia para formar una nueva configuración, excepto la primera y la última celda, ya que representan el punto inicial y final. 
 

El cromosoma original tenía una longitud de ruta igual a INT_MAX , según la entrada definida a continuación, ya que la ruta entre la ciudad 1 y la ciudad 4 no existía. Después de la mutación, el nuevo niño formado tiene una longitud de camino igual a 21 , que es una respuesta mucho más optimizada que la suposición original. Así es como el algoritmo genético optimiza las soluciones a problemas difíciles.
 

A continuación se muestra la implementación del enfoque anterior:
 

C++

// C++ implementation of the above approach
#include <bits/stdc++.h>
#include <limits.h>
using namespace std;
 
// Number of cities in TSP
#define V 5
 
// Names of the cities
#define GENES ABCDE
 
// Starting Node Value
#define START 0
 
// Initial population size for the algorithm
#define POP_SIZE 10
 
// Structure of a GNOME
// string defines the path traversed
// by the salesman while the fitness value
// of the path is stored in an integer
 
struct individual {
    string gnome;
    int fitness;
};
 
// Function to return a random number
// from start and end
int rand_num(int start, int end)
{
    int r = end - start;
    int rnum = start + rand() % r;
    return rnum;
}
 
// Function to check if the character
// has already occurred in the string
bool repeat(string s, char ch)
{
    for (int i = 0; i < s.size(); i++) {
        if (s[i] == ch)
            return true;
    }
    return false;
}
 
// Function to return a mutated GNOME
// Mutated GNOME is a string
// with a random interchange
// of two genes to create variation in species
string mutatedGene(string gnome)
{
    while (true) {
        int r = rand_num(1, V);
        int r1 = rand_num(1, V);
        if (r1 != r) {
            char temp = gnome[r];
            gnome[r] = gnome[r1];
            gnome[r1] = temp;
            break;
        }
    }
    return gnome;
}
 
// Function to return a valid GNOME string
// required to create the population
string create_gnome()
{
    string gnome = "0";
    while (true) {
        if (gnome.size() == V) {
            gnome += gnome[0];
            break;
        }
        int temp = rand_num(1, V);
        if (!repeat(gnome, (char)(temp + 48)))
            gnome += (char)(temp + 48);
    }
    return gnome;
}
 
// Function to return the fitness value of a gnome.
// The fitness value is the path length
// of the path represented by the GNOME.
int cal_fitness(string gnome)
{
    int map[V][V] = { { 0, 2, INT_MAX, 12, 5 },
                    { 2, 0, 4, 8, INT_MAX },
                    { INT_MAX, 4, 0, 3, 3 },
                    { 12, 8, 3, 0, 10 },
                    { 5, INT_MAX, 3, 10, 0 } };
    int f = 0;
    for (int i = 0; i < gnome.size() - 1; i++) {
        if (map[gnome[i] - 48][gnome[i + 1] - 48] == INT_MAX)
            return INT_MAX;
        f += map[gnome[i] - 48][gnome[i + 1] - 48];
    }
    return f;
}
 
// Function to return the updated value
// of the cooling element.
int cooldown(int temp)
{
    return (90 * temp) / 100;
}
 
// Comparator for GNOME struct.
bool lessthan(struct individual t1,
            struct individual t2)
{
    return t1.fitness < t2.fitness;
}
 
// Utility function for TSP problem.
void TSPUtil(int map[V][V])
{
    // Generation Number
    int gen = 1;
    // Number of Gene Iterations
    int gen_thres = 5;
 
    vector<struct individual> population;
    struct individual temp;
 
    // Populating the GNOME pool.
    for (int i = 0; i < POP_SIZE; i++) {
        temp.gnome = create_gnome();
        temp.fitness = cal_fitness(temp.gnome);
        population.push_back(temp);
    }
 
    cout << "\nInitial population: " << endl
        << "GNOME     FITNESS VALUE\n";
    for (int i = 0; i < POP_SIZE; i++)
        cout << population[i].gnome << " "
            << population[i].fitness << endl;
    cout << "\n";
 
    bool found = false;
    int temperature = 10000;
 
    // Iteration to perform
    // population crossing and gene mutation.
    while (temperature > 1000 && gen <= gen_thres) {
        sort(population.begin(), population.end(), lessthan);
        cout << "\nCurrent temp: " << temperature << "\n";
        vector<struct individual> new_population;
 
        for (int i = 0; i < POP_SIZE; i++) {
            struct individual p1 = population[i];
 
            while (true) {
                string new_g = mutatedGene(p1.gnome);
                struct individual new_gnome;
                new_gnome.gnome = new_g;
                new_gnome.fitness = cal_fitness(new_gnome.gnome);
 
                if (new_gnome.fitness <= population[i].fitness) {
                    new_population.push_back(new_gnome);
                    break;
                }
                else {
 
                    // Accepting the rejected children at
                    // a possible probability above threshold.
                    float prob = pow(2.7,
                                    -1 * ((float)(new_gnome.fitness
                                                - population[i].fitness)
                                        / temperature));
                    if (prob > 0.5) {
                        new_population.push_back(new_gnome);
                        break;
                    }
                }
            }
        }
 
        temperature = cooldown(temperature);
        population = new_population;
        cout << "Generation " << gen << " \n";
        cout << "GNOME     FITNESS VALUE\n";
 
        for (int i = 0; i < POP_SIZE; i++)
            cout << population[i].gnome << " "
                << population[i].fitness << endl;
        gen++;
    }
}
 
int main()
{
 
    int map[V][V] = { { 0, 2, INT_MAX, 12, 5 },
                    { 2, 0, 4, 8, INT_MAX },
                    { INT_MAX, 4, 0, 3, 3 },
                    { 12, 8, 3, 0, 10 },
                    { 5, INT_MAX, 3, 10, 0 } };
    TSPUtil(map);
}

Python3

# Python3 implementation of the above approach
from random import randint
 
INT_MAX = 2147483647
# Number of cities in TSP
V = 5
 
# Names of the cities
GENES = "ABCDE"
 
# Starting Node Value
START = 0
 
# Initial population size for the algorithm
POP_SIZE = 10
 
# Structure of a GNOME
# defines the path traversed
# by the salesman while the fitness value
# of the path is stored in an integer
 
 
class individual:
    def __init__(self) -> None:
        self.gnome = ""
        self.fitness = 0
 
    def __lt__(self, other):
        return self.fitness < other.fitness
 
    def __gt__(self, other):
        return self.fitness > other.fitness
 
 
# Function to return a random number
# from start and end
def rand_num(start, end):
    return randint(start, end-1)
 
 
# Function to check if the character
# has already occurred in the string
def repeat(s, ch):
    for i in range(len(s)):
        if s[i] == ch:
            return True
 
    return False
 
 
# Function to return a mutated GNOME
# Mutated GNOME is a string
# with a random interchange
# of two genes to create variation in species
def mutatedGene(gnome):
    gnome = list(gnome)
    while True:
        r = rand_num(1, V)
        r1 = rand_num(1, V)
        if r1 != r:
            temp = gnome[r]
            gnome[r] = gnome[r1]
            gnome[r1] = temp
            break
    return ''.join(gnome)
 
 
# Function to return a valid GNOME string
# required to create the population
def create_gnome():
    gnome = "0"
    while True:
        if len(gnome) == V:
            gnome += gnome[0]
            break
 
        temp = rand_num(1, V)
        if not repeat(gnome, chr(temp + 48)):
            gnome += chr(temp + 48)
 
    return gnome
 
 
# Function to return the fitness value of a gnome.
# The fitness value is the path length
# of the path represented by the GNOME.
def cal_fitness(gnome):
    mp = [
        [0, 2, INT_MAX, 12, 5],
        [2, 0, 4, 8, INT_MAX],
        [INT_MAX, 4, 0, 3, 3],
        [12, 8, 3, 0, 10],
        [5, INT_MAX, 3, 10, 0],
    ]
    f = 0
    for i in range(len(gnome) - 1):
        if mp[ord(gnome[i]) - 48][ord(gnome[i + 1]) - 48] == INT_MAX:
            return INT_MAX
        f += mp[ord(gnome[i]) - 48][ord(gnome[i + 1]) - 48]
 
    return f
 
 
# Function to return the updated value
# of the cooling element.
def cooldown(temp):
    return (90 * temp) / 100
 
 
# Comparator for GNOME struct.
# def lessthan(individual t1,
#               individual t2)
# :
#     return t1.fitness < t2.fitness
 
 
# Utility function for TSP problem.
def TSPUtil(mp):
    # Generation Number
    gen = 1
    # Number of Gene Iterations
    gen_thres = 5
 
    population = []
    temp = individual()
 
    # Populating the GNOME pool.
    for i in range(POP_SIZE):
        temp.gnome = create_gnome()
        temp.fitness = cal_fitness(temp.gnome)
        population.append(temp)
 
    print("\nInitial population: \nGNOME     FITNESS VALUE\n")
    for i in range(POP_SIZE):
        print(population[i].gnome, population[i].fitness)
    print()
 
    found = False
    temperature = 10000
 
    # Iteration to perform
    # population crossing and gene mutation.
    while temperature > 1000 and gen <= gen_thres:
        population.sort()
        print("\nCurrent temp: ", temperature)
        new_population = []
 
        for i in range(POP_SIZE):
            p1 = population[i]
 
            while True:
                new_g = mutatedGene(p1.gnome)
                new_gnome = individual()
                new_gnome.gnome = new_g
                new_gnome.fitness = cal_fitness(new_gnome.gnome)
 
                if new_gnome.fitness <= population[i].fitness:
                    new_population.append(new_gnome)
                    break
 
                else:
 
                    # Accepting the rejected children at
                    # a possible probability above threshold.
                    prob = pow(
                        2.7,
                        -1
                        * (
                            (float)(new_gnome.fitness - population[i].fitness)
                            / temperature
                        ),
                    )
                    if prob > 0.5:
                        new_population.append(new_gnome)
                        break
 
        temperature = cooldown(temperature)
        population = new_population
        print("Generation", gen)
        print("GNOME     FITNESS VALUE")
 
        for i in range(POP_SIZE):
            print(population[i].gnome, population[i].fitness)
        gen += 1
 
 
if __name__ == "__main__":
 
    mp = [
        [0, 2, INT_MAX, 12, 5],
        [2, 0, 4, 8, INT_MAX],
        [INT_MAX, 4, 0, 3, 3],
        [12, 8, 3, 0, 10],
        [5, INT_MAX, 3, 10, 0],
    ]
    TSPUtil(mp)
Producción: 

Initial population: 
GNOME     FITNESS VALUE
043210   24
023410   2147483647
031420   2147483647
034210   31
043210   24
023140   2147483647
032410   2147483647
012340   24
012340   24
032410   2147483647


Current temp: 10000
Generation 1 
GNOME     FITNESS VALUE
013240   21
013240   21
012430   31
012430   31
031240   32
024310   2147483647
013420   2147483647
032140   2147483647
034210   31
012430   31

Current temp: 9000
Generation 2 
GNOME     FITNESS VALUE
031240   32
043210   24
012340   24
042130   32
043210   24
012340   24
034210   31
014320   2147483647
014320   2147483647
023140   2147483647

Current temp: 8100
Generation 3 
GNOME     FITNESS VALUE
013240   21
042310   21
013240   21
013240   21
031240   32
013240   21
012430   31
034120   2147483647
041320   2147483647
043120   2147483647

Current temp: 7290
Generation 4 
GNOME     FITNESS VALUE
031240   32
043210   24
043210   24
043210   24
012340   24
042130   32
013240   21
014320   2147483647
021340   2147483647
043210   24

Current temp: 6561
Generation 5 
GNOME     FITNESS VALUE
043210   24
042310   21
042310   21
013240   21
042310   21
034210   31
013240   21
042310   21
024310   2147483647
024310   2147483647

 

Publicación traducida automáticamente

Artículo escrito por chitrankmishra y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *