# Librairie du projet en version impératif import random import Levenshtein def min_i(array: list[int]) -> int: min_val = array[0] min_i = 0 for i in range(len(array)): if array[i] < min_val: min_val = array[i] min_i = i return min_i def max_i(array: list[int]) -> int: max_val = array[0] max_i = 0 for i in range(len(array)): if array[i] > max_val: max_val = array[i] max_i = i return max_i def new_population(pm, ng, n, ts, tm, alpha, fm): """ fonction qui renvoie une nouvelle population """ population = { "individuals": [new_individual() for i in range(n)], "pm": pm, "ng": ng, "l": len(pm), "n": n, "ts": ts, "tm": tm, "alpha": alpha, "fm": fm } for individual in population["individuals"]: randomize(individual, 4, 30) return population def new_individual(): """ fonction qui renvoie un nouvel individu """ return { "chromozome": "" } def randomize(individual, min_l, max_l) -> str: """ Methode qui change la valeur d'un chromozome pour une valeur aléatoire """ new = "" for i in range(random.randint(min_l, max_l)): new += chr(random.randint(0, 255)) individual["chromozome"] = new def fitness1(individual, pm) -> int: """ Première methode de fitness, fait la somme des différences entre les codages des caractères des deux chaînes. """ sum = 0 for i in range(len(individual["chromozome"])): if i < len(pm) and i < len(individual["chromozome"]): sum += abs(ord(individual["chromozome"][i]) - ord(pm[i])) return -sum def fitness2(individual, pm, alpha) -> int: """ Deuxième methode de fitness qui compte les caractères bien placés et mal placés et qui renvoie un int pondéré par alpha """ match = 0 missed_placed = 0 for i in range(len(individual["chromozome"])): if i >= len(pm): missed_placed += len(individual["chromozome"]) - len(pm) break elif individual["chromozome"][i] == pm[i]: match += 1 else: missed_placed += 1 if len(pm) > len(individual["chromozome"]): missed_placed += len(pm) - len(individual["chromozome"]) return match + alpha * missed_placed def fitness3(individual, pm) -> int: """ Troisième methode de fitness qui utilise la distance de Levenshtein """ return -Levenshtein.distance(individual["chromozome"], pm) def get_fitness(population, individual) -> int: match population["fm"]: case 1: return fitness1(individual, population["pm"]) case 2: return fitness2(individual, population["pm"], population["alpha"]) case 3: return fitness3(individual, population["pm"]) case _: return fitness1(individual, population["pm"]) def get_fitness_list(population): fitness_list = [] for individual in population["individuals"]: fitness_list.append(get_fitness(population, individual)) return fitness_list def get_best(population): """ Methode qui renvoie le meilleur individu de la population """ fitness_list = get_fitness_list(population) return population["individuals"][max_i(fitness_list)] def print_best(population) -> None: """ Methode qui affiche le meilleur individu de la population """ print(get_best(population)["chromozome"]) def select(population) -> None: """ Methode qui sélectionne les meilleurs individus """ fitness_list = get_fitness_list(population) for i in range(int((1 - population["ts"]) * population["n"])): least = min_i(fitness_list) fitness_list.pop(least) population["individuals"].pop(least) def get_two_random_individuals(population): i = random.randint(0, len(population["individuals"]) - 1) j = random.randint(0, len(population["individuals"]) - 1) while i == j: j = random.randint(0, len(population["individuals"]) - 1) return (population["individuals"][i], population['individuals'][j]) def reproduct(population) -> None: """ Methode qui reproduit les individus entre eux jusqu'à obtenir une population de taille N """ new = [] while len(population["individuals"]) + len(new) != population["n"]: indivi_1, indivi_2 = get_two_random_individuals(population) avg = (len(indivi_1) + len(indivi_2)) // 2 cut = random.randint(avg // 3, 2 * avg // 3) while cut > len(indivi_1) or cut > len(indivi_2): cut = random.randint(avg // 3, 2 * avg // 3) new_chromozome = indivi_1["chromozome"][:cut] + indivi_2["chromozome"][cut:] child = new_individual() child["chromozome"] = new_chromozome new.append(child) population["individuals"] += new def mutate(individual) -> None: """ Methode qui change un des caractères du chromozome """ new = list(individual["chromozome"]) dice = random.randint(1,3) if dice == 1 and len(new) < 30: new.insert(random.randint(0, len(new) - 1), chr(random.randint(0, 255))) elif dice == 2 and len(new) > 4: new.pop(random.randint(0, len(new) - 1)) else : new[random.randint(0, len(new) - 1)] = chr(random.randint(0, 255)) individual["chromozome"] = "".join(new) def mutate_pop(population) -> None: """ Methode qui mute une partie de la population selon le taut de mutation """ mutated = [] for i in range(int(population["tm"] * population["n"])): to_mutate = random.randint(0, population["n"] - 1) while to_mutate in mutated: to_mutate = random.randint(0, population["n"] - 1) mutate(population["individuals"][to_mutate]) mutated.append(to_mutate) def run(population) -> None: """ Boucle principale """ for i in range(population["ng"]): select(population) reproduct(population) mutate_pop(population) print_best(population)