|
1 | 1 | # -*- coding: utf-8 -*-
|
2 |
| - |
3 |
| -import os |
4 |
| -import sys |
5 |
| -import random |
6 |
| -import re |
7 |
| -from math import ceil |
8 |
| - |
9 |
| - |
10 |
| -_password = "Toc, toc, toc... Open up. It's me! Who if not?" |
11 |
| - |
12 |
| -############################################################################### |
13 |
| -# Command line options |
14 |
| -############################################################################### |
15 |
| -_pop_size = 100 |
16 |
| -_elite_rate = 0.2 |
17 |
| -_mutate_prob = 0.8 |
18 |
| -_max_generations = 10000 |
19 |
| -_verbose, _print_all, _static_print, _matrix = True, False, True, False |
20 |
| -_version = 0 if os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1] else sys.argv[0][-4] |
21 |
| -for arg in sys.argv[1:]: |
22 |
| - if re.compile('^-[vasVAS]+').search(arg): |
23 |
| - if 'v' in arg: _verbose = True |
24 |
| - if 'V' in arg: _verbose = False |
25 |
| - if 'a' in arg: _print_all = True |
26 |
| - if 'A' in arg: _print_all = False |
27 |
| - if 's' in arg: _static_print = True |
28 |
| - if 'S' in arg: _static_print = False |
29 |
| - if 'm' in arg: _matrix = True |
30 |
| - if 'M' in arg: _matrix = False |
31 |
| - elif re.compile('^--pop-size=[-\+\d]+').search(arg): _pop_size = int(arg.split('=')[1]) |
32 |
| - elif re.compile('^--elite-rate=[-\+\d]+').search(arg): _elite_rate = float(arg.split('=')[1]) |
33 |
| - elif re.compile('^--mutate-prob=[-\+\d]+').search(arg): _mutate_prob = float(arg.split('=')[1]) |
34 |
| - elif re.compile('^--max-generations=[-\+\d]+').search(arg): _max_generations = int(arg.split('=')[1]) |
35 |
| - elif re.compile('^--password=.+').search(arg): _password = arg.split('=')[1] |
36 |
| - elif re.compile('^[^-]{2}.+').search(arg): _password = arg |
37 |
| - elif re.compile('^--verbose=[\d]+').search(arg): _verbose = bool(int(arg.split('=')[1])) |
38 |
| - elif re.compile('^--all=[\d]+').search(arg): _print_all = bool(int(arg.split('=')[1])) |
39 |
| - elif re.compile('^--static=[\d]+').search(arg): _static_print = bool(int(arg.split('=')[1])) |
40 |
| - elif re.compile('^--version=[-\+\d]+').search(arg) and os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1]: _version = arg.split('=')[1] |
41 |
| -if sum([1 for i in [_elite_rate, _mutate_prob, _pop_size, _max_generations] if i < 0]): |
42 |
| - sys.exit("Error: Invalid parameters") |
43 |
| - |
44 |
| -############################################################################### |
45 |
| - |
46 |
| -def get_password_len(): |
47 |
| - """ Return the length of the current password, for simulation purposes """ |
48 |
| - return len(_password) |
49 |
| - |
50 |
| - |
51 |
| -def get_fitness(guess): |
52 |
| - """ Return the number of character's in guess string mismatching the same position of the password """ |
53 |
| - return sum(1 for expected, actual in zip(_password, guess) if expected != actual) |
54 |
| - |
55 |
| - |
56 |
| -def gene_set(): |
57 |
| - """ Return the feasible characters of the password """ |
58 |
| - return " 0123456789áéíóúabcdefghijklmnñopqrstuvwxyzÁÉÍÓÚABCDEFGHIJKLMNÑOPQRSTUVWXYZ!\"#$%&\'()*+,-./:;<=>¿?@[\\]^_`{|}" |
59 |
| - |
60 |
| -############################################################################### |
61 |
| - |
62 |
| -def initial_population(pop_size, chromosome_len): |
63 |
| - """ Create a initial population """ |
64 |
| - population = [] |
65 |
| - for _ in range(pop_size): |
66 |
| - population.append( ''.join(random.choice(gene_set()) for _ in range(chromosome_len))) |
67 |
| - return [[i, get_fitness(i)] for i in population] |
68 |
| - |
69 |
| - |
70 |
| -def mutate(chromosome): |
71 |
| - """ Mutate randomly one gen of the chromosome, which is a string with the characters of the password """ |
72 |
| - pos = random.randint(0, get_password_len() - 1) |
73 |
| - chromosome[0] = chromosome[0][:pos] + random.choice(gene_set()) + chromosome[0][pos+1:] |
74 |
| - chromosome[1] = get_fitness(chromosome[0]) |
75 |
| - return chromosome |
76 |
| - |
77 |
| - |
78 |
| -def crossover(chromosome1, chromosome2): |
79 |
| - """ Perform a one point crossover of the chromosomes """ |
80 |
| - if random.random() < 0.5: chromosome1, chromosome2 = chromosome2, chromosome1 |
81 |
| - split = random.randint(0, get_password_len() - 1) |
82 |
| - hybrid = chromosome1[0][:split] + chromosome2[0][split:] |
83 |
| - return [hybrid, get_fitness(hybrid)] |
84 |
| - |
85 |
| - |
86 |
| -def ga(pop_size=_pop_size, elite_rate=_elite_rate, mutate_prob=_mutate_prob, max_generations=_max_generations, fun_next_gen=None): |
87 |
| - """ Genetic Algorithm driving the search of the password """ |
88 |
| - pop = initial_population(pop_size, get_password_len()) |
89 |
| - for i in range(max_generations): |
90 |
| - pop.sort(key=lambda x: x[1]) |
91 |
| - print_generation(elite_rate, mutate_prob, the_fitest=pop[0], generation=i+1, pop=pop) |
92 |
| - if pop[0][1] == 0: |
93 |
| - return pop[0][0] |
94 |
| - pop = fun_next_gen(pop, pop_size, elite_rate, mutate_prob) |
95 |
| - print("Password not found") |
96 |
| - return False |
97 |
| - |
98 |
| - |
99 |
| -############################################################################### |
100 |
| - |
101 |
| -def roulette(players, num_winners=1): |
102 |
| - """ Return the winners of the roulette wheel selection """ |
103 |
| - selection_mode = num_winners < len(players)/2 # True if it is preferable to select than delete. |
104 |
| - players = players.copy() # Avoid modifying the original list |
105 |
| - players.sort(key=lambda x: x[1], reverse=selection_mode) |
106 |
| - winners = [] if selection_mode else players |
107 |
| - for _ in range(num_winners if selection_mode else len(players) - num_winners): |
108 |
| - domain = gauss_form(len(players)) |
109 |
| - tirada = random.randint(1, domain) |
110 |
| - if selection_mode: winners.append(players.pop( winner(tirada) )) |
111 |
| - else: winners.pop( winner(tirada) ) |
112 |
| - return winners if num_winners != 1 else winners[0] |
113 |
| - |
114 |
| - |
115 |
| -def gauss_form(n): |
116 |
| - """ Return the sum of 1..n natural numbers """ |
117 |
| - return (n*(n+1)) // 2 |
118 |
| - |
119 |
| - |
120 |
| -def inverse_gauss_form(a): |
121 |
| - """ Return the inverse function of the gauss_sum """ |
122 |
| - return ((8*a+1)**0.5 - 1) / 2 |
123 |
| - |
124 |
| - |
125 |
| -def winner(value): |
126 |
| - """ Return the winner of the roulette wheel selection """ |
127 |
| - return ceil( inverse_gauss_form(value) ) - 1 |
128 |
| - |
129 |
| - |
130 |
| -############################################################################### |
131 |
| -# Printing methods |
132 |
| -############################################################################### |
133 |
| - |
134 |
| -def print_generation(elite_rate, mutate_prob, the_fitest, generation, pop): |
135 |
| - msg = "" |
136 |
| - if _static_print: |
137 |
| - msg += "\033[1;1f" |
138 |
| - if _verbose: msg += _print_verbose(elite_rate, mutate_prob, the_fitest, generation, pop) |
139 |
| - else: msg += _print_non_verbose(the_fitest, generation) |
140 |
| - if not _static_print and _verbose and not _print_all: msg += _separator_line() |
141 |
| - if _print_all: msg += _str_print_all(pop) |
142 |
| - if not _static_print and _print_all: msg += _separator_line() |
143 |
| - if _matrix: msg += _print_bests_solutions(pop) |
144 |
| - print(msg) |
145 |
| - |
146 |
| - |
147 |
| -def _str_print_all(pop): |
148 |
| - lst = "\n\nOrdered fitness list of the entire population:\n\033[0;0m" |
149 |
| - for i in range(len(pop)): |
150 |
| - lst += str(f'\033[0;31m{pop[i][1]:4}\033[0;0m') |
151 |
| - if (i+1) % 20 == 0: lst += "\n\033[0;0m" |
152 |
| - return lst |
153 |
| - |
154 |
| - |
155 |
| -def _separator_line(): |
156 |
| - return "\n" + "-"*80 + "\n\033[0;0m" |
157 |
| - |
158 |
| - |
159 |
| -def _print_verbose(elite_rate, mutate_prob, the_fitest, generation, pop): |
160 |
| - ret = "Version: \033[0K\033[5;33m" + str(_version) + "\033[0;0m" |
161 |
| - ret += "\nPassword length: \033[0K\033[5;33m" + str(get_password_len()) + "\033[0;0m" |
162 |
| - ret += "\nPopulation Size: \033[0K\033[5;33m" + str(len(pop)) + "\033[0;0m" |
163 |
| - ret += "\nElite Rate: \033[0K\033[5;33m" + str(elite_rate) + "\033[0;0m" |
164 |
| - ret += "\nMutate Probability: \033[0K\033[5;33m" + str(mutate_prob) + "\033[0;0m" |
165 |
| - ret += "\n\nGeneration: \033[0K\033[5;33m" + str(generation) + "\033[0;0m" |
166 |
| - ret += " best-fitness: \033[0K\033[5;33m" + str(the_fitest[1]) + "\033[0;0m" |
167 |
| - ret += "\n \033[0K\033[5;32m" + str(the_fitest[0]) + "\033[0;0m" |
168 |
| - return ret |
169 |
| - |
170 |
| - |
171 |
| -def _print_non_verbose(the_fitest, generation): |
172 |
| - ret = "\033[0;0mGeneration: " + "\033[0K\033[5;33m" + str(generation) + "\033[0;0m" |
173 |
| - ret += "\033[0;0m best-fitness: " + "\033[0K\033[5;33m" + str(the_fitest[1]) + "\033[0;0m" |
174 |
| - ret += " \033[0K\033[5;32m" + str(the_fitest[0]) + "\033[0;0m" |
175 |
| - return ret |
176 |
| - |
177 |
| - |
178 |
| -def _print_bests_solutions(pop): |
179 |
| - ret = "\n" |
180 |
| - for i in range(40): |
181 |
| - ret += f" \033[5;33m{i:3}\033[0;0m:\033[5;36m{pop[i][1]:4}\033[0;0m | \033[5;32m" \ |
182 |
| - + str(pop[i][0]) + "\n\033[0;0m" |
183 |
| - return ret |
184 |
| - |
185 |
| - |
186 |
| -############################################################################### |
187 |
| -# Version selection |
188 |
| -############################################################################### |
189 |
| - |
190 |
| -if os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1]: |
191 |
| - if _version: |
192 |
| - if _static_print: print("\033[2J\033[1;1f") |
193 |
| - exec(open('ga_v{}.py'.format(_version)).read()) |
194 |
| - else: |
195 |
| - print("\nRun the corresponding version file, or indicate the version you want to run.", |
196 |
| - "Examples:", |
197 |
| - "> python3 ga_v1.py", |
198 |
| - "> python3 ga.py -vasm --version=1\n", sep="\n") |
199 |
| - exit(0) |
200 |
| - |
201 |
| -# print("fin ga.py") |
| 2 | +# Path: ga.py |
| 3 | + |
| 4 | + |
| 5 | +from algorithms.genetics import GeneticAlgorithmV3, GeneticAlgorithmV1, GeneticAlgorithmV2, GeneticAlgorithmV4, \ |
| 6 | + GeneticAlgorithmV5, GeneticAlgorithmV6 |
| 7 | +from utils import args, separator_line |
| 8 | + |
| 9 | +args = args() |
| 10 | + |
| 11 | +if int(args['version']) == 1: |
| 12 | + ga = GeneticAlgorithmV1(vars=args) |
| 13 | +elif int(args['version']) == 2: |
| 14 | + ga = GeneticAlgorithmV2(vars=args) |
| 15 | +elif int(args['version']) == 3: |
| 16 | + ga = GeneticAlgorithmV3(vars=args) |
| 17 | +elif int(args['version']) == 4: |
| 18 | + ga = GeneticAlgorithmV4(vars=args) |
| 19 | +elif int(args['version']) == 5: |
| 20 | + ga = GeneticAlgorithmV5(vars=args) |
| 21 | +elif int(args['version']) == 6: |
| 22 | + ga = GeneticAlgorithmV6(vars=args) |
| 23 | +else: |
| 24 | + raise Exception("Error: Invalid version") |
| 25 | + |
| 26 | +ga.run() |
| 27 | + |
| 28 | +print(f"{separator_line()}\nEnd of version {ga.version}\n") |
| 29 | +ga.print_stats() |
| 30 | +print(f"{separator_line()}\n") |
0 commit comments