Skip to content

Commit cd3d783

Browse files
committed
Entrega
1 parent d5fcf9b commit cd3d783

File tree

7 files changed

+139
-93
lines changed

7 files changed

+139
-93
lines changed

ga.py

+70-74
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,46 @@
44
import sys
55
import random
66
import re
7-
from math import sqrt
87
from math import ceil
98

109

1110
_password = "Abre. Soy yo! Quién va a ser si no?"
1211

13-
12+
###############################################################################
1413
# Opciones de ejecucion
14+
###############################################################################
1515
_pop_size = 100
1616
_elite_rate = 0.2
1717
_mutate_prob = 0.8
1818
_max_generations = 10000
19-
_verbose = True
20-
_print_all = True
21-
_static_print = True
19+
_verbose, _print_all, _static_print = True, False, True
2220
_version = 0 if os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1] else sys.argv[0][-4]
23-
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+
elif re.compile('^--pop-size=[-\+\d]+').search(arg): _pop_size = int(arg.split('=')[1])
30+
elif re.compile('^--elite-rate=[-\+\d]+').search(arg): _elite_rate = float(arg.split('=')[1])
31+
elif re.compile('^--mutate-prob=[-\+\d]+').search(arg): _mutate_prob = float(arg.split('=')[1])
32+
elif re.compile('^--max-generations=[-\+\d]+').search(arg): _max_generations = int(arg.split('=')[1])
33+
elif re.compile('^--password=.+').search(arg): _password = arg.split('=')[1]
34+
elif re.compile('^[^-]{2}.+').search(arg): _password = arg
35+
elif re.compile('^--verbose=[\d]+').search(arg): _verbose = bool(int(arg.split('=')[1]))
36+
elif re.compile('^--all=[\d]+').search(arg): _print_all = bool(int(arg.split('=')[1]))
37+
elif re.compile('^--static=[\d]+').search(arg): _static_print = bool(int(arg.split('=')[1]))
38+
elif re.compile('^--version=[-\+\d]+').search(arg) and os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1]: _version = arg.split('=')[1]
39+
if sum([1 for i in [_elite_rate, _mutate_prob, _pop_size, _max_generations] if i < 0]):
40+
sys.exit("Error: Invalid parameters")
41+
42+
43+
44+
###############################################################################
45+
# Metodos aportados por el profesor (No se modifican)
46+
###############################################################################
2447

2548
def get_password_len():
2649
""" Return the length of the current password, for simulation purposes """
@@ -37,75 +60,53 @@ def gene_set():
3760
return " 0123456789áéíóúabcdefghijklmnñopqrstuvwxyzÁÉÍÓÚABCDEFGHIJKLMNÑOPQRSTUVWXYZ!\"#$%&\'()*+,-./:;<=>¿?@[\\]^_`{|}"
3861

3962

40-
def procese_options():
41-
global _password, _pop_size, _elite_rate, _mutate_prob, _max_generations, _verbose, _print_all, _static_print, _version
42-
# if ('--verbose') in sys.argv: _verbose = True
43-
# if ('--all') in sys.argv: _print_all = True
44-
# if ('--static') in sys.argv: _static_print = True
45-
for arg in sys.argv[1:]:
46-
if re.compile('^-[vasVAS]+').search(arg):
47-
if 'v' in arg: _verbose = True
48-
if 'V' in arg: _verbose = False
49-
if 'a' in arg: _print_all = True
50-
if 'A' in arg: _print_all = False
51-
if 's' in arg: _static_print = True
52-
if 'S' in arg: _static_print = False
53-
elif re.compile('^--pop-size=[-\+\d]+').search(arg): _pop_size = int(arg.split('=')[1])
54-
elif re.compile('^--elite-rate=[-\+\d]+').search(arg): _elite_rate = float(arg.split('=')[1])
55-
elif re.compile('^--mutate-prob=[-\+\d]+').search(arg): _mutate_prob = float(arg.split('=')[1])
56-
elif re.compile('^--max-generations=[-\+\d]+').search(arg): _max_generations = int(arg.split('=')[1])
57-
elif re.compile('^--password=.+').search(arg): _password = arg.split('=')[1]
58-
elif re.compile('^[^-]{2}.+').search(arg): _password = arg
59-
elif re.compile('^--verbose=[\d]+').search(arg):
60-
_verbose = bool(int(arg.split('=')[1]))
61-
elif re.compile('^--all=[\d]+').search(arg):
62-
_print_all = bool(int(arg.split('=')[1]))
63-
elif re.compile('^--static=[\d]+').search(arg):
64-
_static_print = bool(int(arg.split('=')[1]))
65-
elif re.compile('^--version=[-\+\d]+').search(arg) and os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1]:
66-
_version = arg.split('=')[1]
67-
if sum([1 for i in [_elite_rate, _mutate_prob, _pop_size, _max_generations] if i < 0]):
68-
sys.exit("Error: Invalid parameters")
69-
70-
71-
procese_options()
7263

64+
###############################################################################
65+
# Implementación metodos solicitados por el profesor
66+
###############################################################################
7367

7468
def initial_population(pop_size, chromosome_len):
7569
""" Create a initial population """
76-
# return [random.sample(gene_set(), chromosome_len) for _ in range(pop_size)]
77-
# sample devuelve conjuntos de k elementos sin elementos repetidos
78-
return [[random.choice(gene_set()) for _ in range(chromosome_len)] for _ in range(pop_size)]
70+
population = []
71+
for _ in range(pop_size):
72+
population.append( ''.join(random.choice(gene_set()) for _ in range(chromosome_len)))
73+
return [[i, get_fitness(i)] for i in population]
7974

8075

81-
# Implemente la función mutate() que recibe un cromosoma y lo muta cambiando aleatoriamente un único gen por otro del gene_set().
8276
def mutate(chromosome):
8377
""" Mutate randomly one gen of the chromosome, which is a string with the characters of the password """
84-
chromosome[random.randint(0, len(chromosome) - 1)] = random.choice(gene_set())
78+
pos = random.randint(0, get_password_len() - 1)
79+
chromosome[0] = chromosome[0][:pos] + random.choice(gene_set()) + chromosome[0][pos+1:]
80+
chromosome[1] = get_fitness(chromosome[0])
8581
return chromosome
8682

8783

88-
# Implemente la función crossover() que recibe 2 cromosomas, realiza con ellos una hibridación de 1 punto aleatorio, y devuelve otro cromosoma con la hibridación realizada.
8984
def crossover(chromosome1, chromosome2):
9085
""" Perform a one point crossover of the chromosomes """
9186
if random.random() < 0.5: chromosome1, chromosome2 = chromosome2, chromosome1
9287
split = random.randint(0, get_password_len() - 1)
93-
return chromosome1[:split] + chromosome2[split:]
88+
hybrid = chromosome1[0][:split] + chromosome2[0][split:]
89+
return [hybrid, get_fitness(hybrid)]
9490

9591

9692
def ga(pop_size=_pop_size, elite_rate=_elite_rate, mutate_prob=_mutate_prob, max_generations=_max_generations, fun_next_gen=None):
9793
""" Genetic Algorithm driving the search of the password """
9894
pop = initial_population(pop_size, get_password_len())
9995
for i in range(max_generations):
100-
pop.sort(key=get_fitness)
96+
pop.sort(key=lambda x: x[1])
10197
print_generation(elite_rate, mutate_prob, the_fitest=pop[0], generation=i+1, pop=pop)
102-
if get_fitness(pop[0]) == 0:
103-
return pop[0]
98+
if pop[0][1] == 0:
99+
return pop[0][0]
104100
pop = fun_next_gen(pop, pop_size, elite_rate, mutate_prob)
105101
print("Password not found")
106102
return False
107103

108104

105+
106+
###############################################################################
107+
# Metodos propios
108+
###############################################################################
109+
109110
def _clear():
110111
if os.name == "nt": os.system("cls")
111112
else: os.system("clear")
@@ -116,58 +117,53 @@ def print_generation(elite_rate, mutate_prob, the_fitest, generation, pop):
116117
if _verbose:
117118
print("Version:", _version, "\nPassword length:", get_password_len(),
118119
"\npop_size:", len(pop), "\nelite_rate:", elite_rate, "\nmutate_prob:", mutate_prob,
119-
"\n\nGeneration:", generation, "best-fitness:", get_fitness(the_fitest),
120-
"\n", ''.join(the_fitest))
120+
"\n\nGeneration:", generation, "best-fitness:", the_fitest[1],
121+
"\n", the_fitest[0])
121122
else:
122-
print("Generation:", generation, " best-fitness:", get_fitness(the_fitest),
123-
' ', ''.join(the_fitest), sep=" ")
123+
print("Generation:", generation, " best-fitness:", the_fitest[1],
124+
' ', the_fitest[0], sep=" ")
124125
if not _static_print and _verbose and not _print_all: print("-"*80)
125126
if _print_all:
126127
print("\nListado ordenado de fitness de toda la poblacion:")
127-
print(*['{0:{width}}'.format(get_fitness(i), width=2) for i in pop], sep=" ")
128+
print(*['{0:{width}}'.format(i[1], width=2) for i in pop], sep=" ")
128129
if not _static_print and _print_all: print("-"*80)
129130

130131

131-
# def chromosome_to_string(chromosome):
132-
# """ Return the string representation of the chromosome """
133-
# return "".join(chromosome)
134-
135-
136132
def roulette(players, num_winers=1):
137133
""" Return the winners of the roulette wheel selection """
138-
selection_mode = num_winers < len(players)/2
139-
players = players.copy()
140-
players.sort(key=get_fitness, reverse=selection_mode)
134+
selection_mode = num_winers < len(players)/2 # True si es mejor seleccionar que eliminar
135+
players = players.copy() # Evitar modificar la lista original
136+
players.sort(key=lambda x: x[1], reverse=selection_mode)
141137
winners = [] if selection_mode else players
142138
for _ in range(num_winers if selection_mode else len(players) - num_winers):
143139
domain = gauss_form(len(players))
144140
tirada = random.randint(1, domain)
145-
if selection_mode:
146-
winners.append(players.pop(ceil(inverse_gauss_form(tirada))-1))
147-
else:
148-
winners.pop(ceil(inverse_gauss_form(tirada))-1)
149-
return winners if len(winners) > 1 else winners[0]
141+
if selection_mode: winners.append(players.pop( winner(tirada) ))
142+
else: winners.pop( winner(tirada) )
143+
return winners if num_winers != 1 else winners[0]
150144

151145

152146
def gauss_form(n):
153147
""" Return the sum of 1..n natural numbers """
154148
return (n*(n+1)) // 2
155149

156150

157-
def inverse_gauss_form(x):
151+
def inverse_gauss_form(a):
158152
""" Return the inverse function of the gauss_sum """
159-
return ((8*x+1)**0.5 - 1) / 2
153+
return ((8*a+1)**0.5 - 1) / 2
154+
155+
156+
def winner(value):
157+
""" Return the winner of the roulette wheel selection """
158+
return ceil( inverse_gauss_form(value) ) - 1
160159

160+
###############################################################################
161+
# Selección de versión
162+
###############################################################################
161163

162-
# print("os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1]", os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1])
163-
# print(" __file__", __file__ )
164-
# print("sys.argv[0]", sys.argv[0])
165-
# print("sys.argv", sys.argv)
166164
if os.path.split(__file__)[-1] == os.path.split(sys.argv[0])[-1]:
167165
if _version:
168166
exec(open('ga_v{}.py'.format(_version)).read())
169-
# print("os.execv(sys.executable, sys.argv):")
170-
# os.execv(sys.executable, [sys.argv[0], [sys.argv[1:]]])
171167
else:
172168
print("\nEjecute el archivo correspondiente de la versión, o indique la versión que dese ejecutar.",
173169
"Ejemplos:",

ga_v1.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,19 @@
22

33
import random
44

5-
from ga import ga, crossover, mutate
5+
from ga import ga, crossover, mutate, roulette
66

77

88
def next_generation(pop, pop_size, elite_rate, mutate_prob):
99
""" Return the next generation """
10-
pop = pop[:2] if pop_size*elite_rate<3 else pop[:int(pop_size*elite_rate)] # Selecciona la elite
10+
pop = pop[:max(2, int(pop_size*elite_rate))] # Selecciona la elite, mínimo 2
1111
while len(pop) < pop_size: # Cruzar elite hasta completar la población
12-
couple = random.sample(pop, 2)
12+
couple = roulette(pop, 2)
1313
pop.append(crossover(couple[0], couple[1]))
14-
# Mutar la poblacion segun la probabilidad de mutate_prob
15-
pop = [mutate(pop[i]) if random.random() < mutate_prob else pop[i]
16-
for i in range(len(pop))]
14+
# Mutar la población según la probabilidad de mutate_prob
15+
pop = [mutate(pop[i]) if random.random() < mutate_prob else pop[i] for i in range(len(pop))]
1716
return pop
1817

1918

20-
gpassword = ga(#pop_size=100, elite_rate=0.2, mutate_prob=0.8,
21-
fun_next_gen=next_generation)
19+
gpassword = ga(fun_next_gen=next_generation)
2220
print("Fin version 1")

ga_v2.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77

88
def next_generation(pop, pop_size, elite_rate, mutate_prob):
99
""" Return the next generation """
10-
split = int(pop_size * elite_rate) # Selecciona la elite
10+
split = min(pop_size-2, int(pop_size * elite_rate)) # Selecciona la elite (maximo pop_size-2)
1111
elite, non_elite = pop[:split], pop[split:]
1212
# Mutar entre los no elite
13-
non_elite = [non_elite[i] if random.random() > mutate_prob else mutate(non_elite[i]) for i in range(len(non_elite))]
13+
for i in non_elite:
14+
if random.random() > mutate_prob: mutate(i)
1415
# Cruzar para generar descendencia. Se cruza por parejas en orden para cruzar individuos de similar fitness
1516
progeny = [crossover(pop[i], pop[i+1]) for i in range(0, pop_size, 2)]
1617
return elite + roulette(non_elite + progeny, num_winers=pop_size - len(elite))

ga_v3.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88

99
def next_generation(pop, pop_size, elite_rate, mutate_prob):
1010
""" Return the next generation """
11-
split = int(pop_size * elite_rate)
11+
split = min(pop_size-2, int(pop_size * elite_rate)) # (maximo pop_size-2)
1212
elite, non_elite = pop[:split], pop[split:] # Selecciona la elite
1313
progeny = []
1414
for i in elite:
1515
if random.random() < mutate_prob: mutate(i)
16-
else: progeny.append(crossover(i, elite[random.randint(0, len(elite) - 1)]))
16+
else: progeny.append(crossover(i, roulette(non_elite)))
1717
return elite + roulette(non_elite + progeny, num_winers=pop_size - len(elite))
1818

1919

ga_v4.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@
77

88
def next_generation(pop, pop_size, elite_rate, mutate_prob):
99
""" Return the next generation """
10-
elite = pop[:int(pop_size * elite_rate)] # Selecciona la elite
11-
progeny = elite
12-
while len(progeny) < pop_size:
10+
elite = pop[:max(2, int(pop_size*elite_rate))] # Selecciona la elite, mínimo 2
11+
nextgen = elite
12+
while len(nextgen) < pop_size:
1313
parent1, parent2 = roulette(elite), roulette(pop)
14-
# pop.remove(parent2)
1514
child = crossover(parent1, parent2)
16-
if random.random() < mutate_prob: child = mutate(child)
17-
progeny.append(child)
18-
return progeny
15+
if random.random() < mutate_prob: mutate(child)
16+
nextgen.append(child)
17+
return nextgen
1918

2019

2120
gpassword = ga(#pop_size=100, elite_rate=0.8, mutate_prob=0.2,

ga_v5.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import random
4+
5+
from ga import ga, crossover, mutate, roulette
6+
7+
8+
def next_generation(pop, pop_size, elite_rate, mutate_prob):
9+
""" Return the next generation """
10+
split = max(10, int(pop_size*elite_rate))
11+
elite, non_elite = pop[:split], pop[split:] # Selecciona la elite
12+
nextgen = elite.copy()
13+
to_cross = []
14+
while len(elite) > 0:
15+
i = roulette(elite)
16+
if random.random() < mutate_prob: nextgen.append(mutate(i.copy()))
17+
else: to_cross.append(i)
18+
elite.remove(i)
19+
if len(to_cross) % 2 == 1: to_cross.append(roulette(non_elite))
20+
while len(to_cross) > 0:
21+
parent1, parent2 = roulette(to_cross, 2)
22+
nextgen.append(crossover(parent1, parent2))
23+
to_cross.remove(parent1)
24+
to_cross.remove(parent2)
25+
return nextgen + roulette(non_elite, pop_size - len(nextgen))
26+
27+
28+
gpassword = ga(fun_next_gen=next_generation)
29+
print("Fin version 5")

ga_v6.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import random
4+
5+
from ga import ga, crossover, mutate, roulette
6+
7+
8+
def next_generation(pop, pop_size, elite_rate, mutate_prob):
9+
""" Return the next generation """
10+
nextgen = pop[:int(pop_size*elite_rate)] # Selecciona la elite
11+
parents = roulette(pop, random.randint(0, pop_size))
12+
while len(parents) > 2:
13+
parent1, parent2 = roulette(parents, 2)
14+
parents.remove(parent1)
15+
parents.remove(parent2)
16+
child = crossover(parent1, parent2)
17+
if random.random() < mutate_prob: mutate(child)
18+
nextgen.append(child)
19+
return nextgen + roulette(pop, pop_size - len(nextgen))
20+
21+
22+
gpassword = ga(fun_next_gen=next_generation)
23+
print("Fin version 6")

0 commit comments

Comments
 (0)