diff --git a/lib/ultra_mastermind_imp.py b/lib/ultra_mastermind_imp.py index e69de29..8468a09 100644 --- a/lib/ultra_mastermind_imp.py +++ b/lib/ultra_mastermind_imp.py @@ -0,0 +1,176 @@ +# 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): # -> set(list(set(str)), str, int, int, int, float, float, float, int) + """ + 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, len(pm)) + + return population + +def new_individual(): # -> set(str) + """ + fonction qui renvoie un nouvel individu + """ + return { + "chromozome": "" + } + +def select(population) -> None: + """ + Methode qui sélectionne les meilleurs individus + """ + fitness_list = [] + for individual in population["individuals"]: + match population["fm"]: + case 1: + fitness_list.append(fitness1(individual, population["pm"])) + case 2: + fitness_list.append(fitness2(individual, population["pm"], population["alpha"])) + case 3: + fitness_list.append(fitness3(individual, population["pm"])) + case _: + fitness_list.append(fitness1(individual, population["pm"])) + + for i in range(int((1 - population["ts"]) * population["n"])): + least = min_i(fitness_list) + fitness_list.pop(least) + population["individuals"].pop(least) + +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"]: + cut = random.randint(int(population["l"] / 3), int(2 * population["l"] / 3)) + 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) + indivi_1 = population["individuals"][i] + indivi_2 = population['individuals'][j] + 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_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 print_best(population) -> None: + """ + Methode qui affiche le meilleur individu de la population + """ + fitness_list = [] + for individual in population["individuals"]: + match population["fm"]: + case 1: + fitness_list.append(fitness1(individual, population["pm"])) + case 2: + fitness_list.append(fitness2(individual, population["pm"], population["alpha"])) + case 3: + fitness_list.append(fitness3(individual, population["pm"])) + case _: + fitness_list.append(fitness1(individual, population["pm"])) + print(population["individuals"][max_i(fitness_list)]["chromozome"]) + +def run(population) -> None: + """ + Boucle principale + """ + for i in range(population["ng"]): + select(population) + reproduct(population) + mutate_pop(population) + print_best(population) + +def randomize(individual, l) -> str: + """ + 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)) + 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"])): + 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 individual["chromozome"][i] == pm[i]: + match += 1 + else: + missed_placed += 1 + 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 mutate(individual) -> None: + """ + Methode qui change un des caractères du chromozome + """ + new = list(individual["chromozome"]) + new[random.randint(0, len(new) - 1)] = chr(random.randint(0, 255)) + individual["chromozome"] = "".join(new) diff --git a/main.py b/main.py index 15d8142..552c25b 100644 --- a/main.py +++ b/main.py @@ -2,21 +2,24 @@ # project libs importations import lib.ultra_mastermind_obj as libobj -import lib.ultra_mastermind_imp as libomp +import lib.ultra_mastermind_imp as libimp # constants PM = "Hello, world!" -NG = 1000 +NG = 2000 N = 400 TS = 0.5 TM = 0.01 ALPHA = 0.5 -FITNESS_METHOD = 3 +FITNESS_METHOD = 1 # main function def main() -> None: pop = libobj.Population(pm = PM, ng = NG, n = N, ts = TS, tm = TM, alpha = ALPHA, fm = FITNESS_METHOD) pop.run() + pop = libimp.new_population(PM, NG, N, TS, TM, ALPHA, FITNESS_METHOD) + libimp.run(pop) + if __name__ == "__main__": main()