21
21
random .seed (random .randint (0 , 1000 ))
22
22
23
23
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
+
24
72
def basic (target : str , genes : list [str ], debug : bool = True ) -> tuple [int , int , str ]:
25
73
"""
26
74
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,
70
118
total_population += len (population )
71
119
72
120
# 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 ))
84
121
85
122
# Adding a bit of concurrency can make everything faster,
86
123
#
@@ -94,7 +131,7 @@ def evaluate(item: str, main_target: str = target) -> tuple[str, float]:
94
131
#
95
132
# but with a simple algorithm like this, it will probably be slower.
96
133
# 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 ]
98
135
99
136
# Check if there is a matching evolution.
100
137
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]:
121
158
(item , score / len (target )) for item , score in population_score
122
159
]
123
160
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
-
156
161
# This is selection
157
162
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 ))
159
164
# Check if the population has already reached the maximum value and if so,
160
165
# break the cycle. If this check is disabled, the algorithm will take
161
166
# forever to compute large strings, but will also calculate small strings in
0 commit comments