# Librairie du projet en version orientée objet 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 class Population: """ Classe qui représente notre population d'individuts """ def __init__(self, pm, ng, n, ts, tm, alpha, fm): self.individuals = [Individual() for _ in range(n)] for individual in self.individuals: individual.randomize(len(pm)) self.pm = pm self.ng = ng self.l = len(pm) self.n = n self.ts = ts self.tm = tm self.alpha = alpha self.fitness_method = fm def select(self) -> None: """ Methode qui sélectionne les meilleurs individus """ fitness_list = [] for individual in self.individuals: match self.fitness_method: case 1: fitness_list.append(individual.fitness1(self.pm)) case 2: fitness_list.append(individual.fitness2(self.pm, self.alpha)) case 3: fitness_list.append(individual.fitness3(self.pm)) case _: fitness_list.append(individual.fitness1(self.pm)) for i in range(int((1 - self.ts) * self.n)): least = min_i(fitness_list) fitness_list.pop(least) self.individuals.pop(least) def reproduct(self) -> None: """ Methode qui reproduit les individus entre eux jusqu'à obtenir une population de taille N """ new = [] while len(self.individuals) + len(new) != self.n: cut = random.randint(int(self.l / 3), int(2 * self.l / 3)) indivi_1 = self.individuals[random.randint(0, len(self.individuals) - 1)] indivi_2 = self.individuals[random.randint(0, len(self.individuals) - 1)] while indivi_1 == indivi_2: indivi_2 = self.individuals[random.randint(0, len(self.individuals) - 1)] new_chromozome = indivi_1.getChromozome()[:cut] + indivi_2.getChromozome()[cut:] child = Individual() child.setChromozome(new_chromozome) new.append(child) self.individuals += new def mutate(self) -> None: """ Methode qui mute une partie de la population selon le taut de mutation """ mutated = [] for i in range(int(self.tm * self.n)): to_mutate = random.randint(0, self.n - 1) while to_mutate in mutated: to_mutate = random.randint(0, self.n - 1) self.individuals[to_mutate].mutate() mutated.append(to_mutate) def print_best(self) -> None: """ Methode qui affiche le meilleur individu de la population """ fitness_list = [] for individual in self.individuals: match self.fitness_method: case 1: fitness_list.append(individual.fitness1(self.pm)) case 2: fitness_list.append(individual.fitness2(self.pm, self.alpha)) case 3: fitness_list.append(individual.fitness3(self.pm)) case _: fitness_list.append(individual.fitness1(self.pm)) print(self.individuals[max_i(fitness_list)].getChromozome()) def run(self) -> None: """ Boucle principale """ for i in range(self.ng): self.select() self.reproduct() self.mutate() self.print_best() class Individual: """ Classe qui représente les individuts de la population (les solutions potentielles) """ def __init__(self): self.chromozome = "" def setChromozome(self, c: str) -> None: self.chromozome = c def getChromozome(self) -> str: return self.chromozome def randomize(self, l) -> None: """ Methode qui change la valeur d'un chromozome pour une valeur aléatoire """ new = "" for i in range(l): new += chr(random.randint(0, 255)) self.chromozome = new def fitness1(self, 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(self.chromozome)): sum += abs(ord(self.chromozome[i]) - ord(pm[i])) return -sum def fitness2(self, 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(self.chromozome)): if self.chromozome[i] == pm[i]: match += 1 else: missed_placed += 1 return match + alpha * missed_placed def fitness3(self, pm) -> int: """ Troisième methode de fitness qui utilise la distance de Levenshtein """ return -Levenshtein.distance(self.chromozome, pm) def mutate(self) -> None: """ Methode qui change un des caractères du chromozome """ new = list(self.chromozome) new[random.randint(0, len(new) - 1)] = chr(random.randint(0, 255)) self.chromozome = "".join(new)