2121random .seed (random .randint (0 , 1000 ))
2222
2323
24+ def evaluate (item : str , main_target : str ) -> tuple [str , float ]:
25+ """
26+ Evaluate how similar the item is with the target by just
27+ counting each char in the right position
28+ >>> evaluate("Helxo Worlx", "Hello World")
29+ ('Helxo Worlx', 9.0)
30+ """
31+ score = len ([g for position , g in enumerate (item ) if g == main_target [position ]])
32+ return (item , float (score ))
33+
34+
35+ def crossover (parent_1 : str , parent_2 : str ) -> tuple [str , str ]:
36+ """Slice and combine two string at a random point."""
37+ random_slice = random .randint (0 , len (parent_1 ) - 1 )
38+ child_1 = parent_1 [:random_slice ] + parent_2 [random_slice :]
39+ child_2 = parent_2 [:random_slice ] + parent_1 [random_slice :]
40+ return (child_1 , child_2 )
41+
42+
43+ def mutate (child : str , genes : list [str ]) -> str :
44+ """Mutate a random gene of a child with another one from the list."""
45+ child_list = list (child )
46+ if random .uniform (0 , 1 ) < MUTATION_PROBABILITY :
47+ child_list [random .randint (0 , len (child )) - 1 ] = random .choice (genes )
48+ return "" .join (child_list )
49+
50+
51+ # Select, crossover and mutate a new population.
52+ def select (
53+ parent_1 : tuple [str , float ],
54+ population_score : list [tuple [str , float ]],
55+ genes : list [str ],
56+ ) -> list [str ]:
57+ """Select the second parent and generate new population"""
58+ pop = []
59+ # Generate more children proportionally to the fitness score.
60+ child_n = int (parent_1 [1 ] * 100 ) + 1
61+ child_n = 10 if child_n >= 10 else child_n
62+ for _ in range (child_n ):
63+ parent_2 = population_score [random .randint (0 , N_SELECTED )][0 ]
64+
65+ child_1 , child_2 = crossover (parent_1 [0 ], parent_2 )
66+ # Append new string to the population list.
67+ pop .append (mutate (child_1 , genes ))
68+ pop .append (mutate (child_2 , genes ))
69+ return pop
70+
71+
2472def basic (target : str , genes : list [str ], debug : bool = True ) -> tuple [int , int , str ]:
2573 """
2674 Verify that the target contains no genes besides the ones inside genes variable.
@@ -70,17 +118,6 @@ def basic(target: str, genes: list[str], debug: bool = True) -> tuple[int, int,
70118 total_population += len (population )
71119
72120 # Random population created. Now it's time to evaluate.
73- def evaluate (item : str , main_target : str = target ) -> tuple [str , float ]:
74- """
75- Evaluate how similar the item is with the target by just
76- counting each char in the right position
77- >>> evaluate("Helxo Worlx", Hello World)
78- ["Helxo Worlx", 9]
79- """
80- score = len (
81- [g for position , g in enumerate (item ) if g == main_target [position ]]
82- )
83- return (item , float (score ))
84121
85122 # Adding a bit of concurrency can make everything faster,
86123 #
@@ -94,7 +131,7 @@ def evaluate(item: str, main_target: str = target) -> tuple[str, float]:
94131 #
95132 # but with a simple algorithm like this, it will probably be slower.
96133 # We just need to call evaluate for every item inside the population.
97- population_score = [evaluate (item ) for item in population ]
134+ population_score = [evaluate (item , target ) for item in population ]
98135
99136 # Check if there is a matching evolution.
100137 population_score = sorted (population_score , key = lambda x : x [1 ], reverse = True )
@@ -121,41 +158,9 @@ def evaluate(item: str, main_target: str = target) -> tuple[str, float]:
121158 (item , score / len (target )) for item , score in population_score
122159 ]
123160
124- # Select, crossover and mutate a new population.
125- def select (parent_1 : tuple [str , float ]) -> list [str ]:
126- """Select the second parent and generate new population"""
127- pop = []
128- # Generate more children proportionally to the fitness score.
129- child_n = int (parent_1 [1 ] * 100 ) + 1
130- child_n = 10 if child_n >= 10 else child_n
131- for _ in range (child_n ):
132- parent_2 = population_score [ # noqa: B023
133- random .randint (0 , N_SELECTED )
134- ][0 ]
135-
136- child_1 , child_2 = crossover (parent_1 [0 ], parent_2 )
137- # Append new string to the population list.
138- pop .append (mutate (child_1 ))
139- pop .append (mutate (child_2 ))
140- return pop
141-
142- def crossover (parent_1 : str , parent_2 : str ) -> tuple [str , str ]:
143- """Slice and combine two string at a random point."""
144- random_slice = random .randint (0 , len (parent_1 ) - 1 )
145- child_1 = parent_1 [:random_slice ] + parent_2 [random_slice :]
146- child_2 = parent_2 [:random_slice ] + parent_1 [random_slice :]
147- return (child_1 , child_2 )
148-
149- def mutate (child : str ) -> str :
150- """Mutate a random gene of a child with another one from the list."""
151- child_list = list (child )
152- if random .uniform (0 , 1 ) < MUTATION_PROBABILITY :
153- child_list [random .randint (0 , len (child )) - 1 ] = random .choice (genes )
154- return "" .join (child_list )
155-
156161 # This is selection
157162 for i in range (N_SELECTED ):
158- population .extend (select (population_score [int (i )]))
163+ population .extend (select (population_score [int (i )], population_score , genes ))
159164 # Check if the population has already reached the maximum value and if so,
160165 # break the cycle. If this check is disabled, the algorithm will take
161166 # forever to compute large strings, but will also calculate small strings in
0 commit comments