#VERSIÓN 4: VERSIÓN SEMI-ORIENTADA A OBJETOS #(F): A añadir en (F)uturas versiones ### FUNCIONES AUXILIARES ### import random from .Reparto import Reparto def mejor_individuo(list_individuos : list[Reparto]) -> Reparto: #paralelizable mejor_individuo = list_individuos[0] for individuo in list_individuos[1:]: mejor_individuo = individuo if individuo.coste < mejor_individuo.coste else mejor_individuo #print("mejor_individuo: ", mejor_individuo.ruta, mejor_individuo.coste) return mejor_individuo class Evolucion: @staticmethod def elitismo(poblacion : list[Reparto]) -> Reparto: #mejor ruta de la poblacion return mejor_individuo(poblacion) @staticmethod def torneo(poblacion : list[Reparto], tamano_torneo) -> list[Reparto]: #mejor ruta de una parte de la poblacion selected_individuos = random.sample(poblacion, tamano_torneo) return mejor_individuo(selected_individuos) @staticmethod def mutate(ruta : Reparto) -> Reparto: #intercambia dos posiciones en la ruta idx1, idx2 = random.sample(range(Reparto.grafo.num_nodos + Reparto.vehiculos.num_vehiculos - 2), 2) lista = ruta.ruta.copy() lista[idx1], lista[idx2] = lista[idx2], lista[idx1] return Reparto(lista) @staticmethod def crossover(parent1 : Reparto, parent2 : Reparto) -> Reparto: #cruza dos rutas, pegando un cacho de la primera en la segunda num_total = Reparto.grafo.num_nodos + Reparto.vehiculos.num_vehiculos - 2 #-1 por el Almacén #-1 porque si hay n vehiculos, solo añadimos n-1 simbolos start = random.randint(0, num_total - 1) #random.randint(a, b) toma un número entero al azar entre [a,b], no entre [a,b) end = random.randint(start, num_total - 1) child = [None] * num_total for i in range(start, end+1): child[i] = parent1.ruta[i] remaining = [item for item in parent2.ruta if item not in child] remaining_index = 0 for i in range(num_total): if child[i] == None: child[i] = remaining[remaining_index] remaining_index += 1 return Reparto(child)