You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -20,7 +20,9 @@ The randomization that allows for organisms to change over time. In GAs we build
20
20
Simply reproduction. A generation will a mixed representation of the previous generation, with offspring taking data (DNA) from both parents. GAs do this by randomly, but weightily, mating offspring to create new generations.
21
21
22
22
### Resources:
23
-
*[Wikipedia]()
23
+
*[Genetic Algorithms in Search Optimization, and Machine Learning](https://www.amazon.com/Genetic-Algorithms-Optimization-Machine-Learning/dp/0201157675/ref=sr_1_sc_1?ie=UTF8&qid=1520628364&sr=8-1-spell&keywords=Genetic+Algortithms+in+search)
The above function takes a list of individuals with their calculated fitness. Then selects one at random offset by their fitness value.
146
+
The above function takes a list of individuals with their calculated fitness. Then selects one at random offset by their fitness value. The horrible 1,000,000 multiplication and division is to insure precision by calculating decimals. `arc4random` only uses integers so this is required to convert to a precise Double, it's not perfect, but enough for our example.
145
147
146
148
## Mutation
147
149
@@ -168,19 +170,16 @@ This allows for a population to explore all the possibilities of it's building b
168
170
169
171
## Crossover
170
172
171
-
Crossover, the sexy part of a GA, is how offspring are created from 2 selected individuals in the current population. This is done by splitting the parents into 2 parts, then combining 1 part from each parent to create the offspring. To promote diversity, we randomly select a index to split the parents:
173
+
Crossover, the sexy part of a GA, is how offspring are created from 2 selected individuals in the current population. This is done by splitting the parents into 2 parts, then combining 1 part from each parent to create the offspring. To promote diversity, we randomly select a index to split the parents.
@@ -203,7 +202,7 @@ for generation in 0...GENERATIONS {
203
202
}
204
203
```
205
204
206
-
Now, for each individual in the population, we need to calculate its fitness and weighted value. For weighted choice we store the fitness as a percent`1 / fitness`.
205
+
Now, for each individual in the population, we need to calculate its fitness and weighted value. Since 0 is the best value we will use `1/fitness` to represent the weighted value. Note this is not a percent, but just how much more likely the value is to be selected over others. If the highest number was the most fit, the weight calculation would be `fitness/totalFitness`, which would be a percent.
207
206
208
207
```swift
209
208
var weightedPopulation = [(item:[UInt8], weight:Double)]()
@@ -215,24 +214,118 @@ for individual in population {
215
214
}
216
215
```
217
216
218
-
To understand weighted choice, let walk though a smaller example, let's say we have the following population, where 0 is the best fitness:
217
+
From here we can start to build the next generation.
219
218
220
-
```txt
221
-
1: 10
222
-
2: 5
223
-
3: 4
224
-
4: 7
225
-
5: 11
219
+
```swift
220
+
var nextGeneration = []
221
+
```
222
+
223
+
The below loop is where we pull everything together. We loop for `POP_SIZE`, selecting 2 individuals by weighted choice, crossover their values to produce a offspring, then finial subject the new individual to mutation. Once completed we have a completely new generation based on the last generation.
224
+
225
+
```swift
226
+
0...POP_SIZE).forEach { _in
227
+
let ind1 =weightedChoice(items: weightedPopulation)
228
+
let ind2 =weightedChoice(items: weightedPopulation)
229
+
230
+
let offspring =crossover(dna1: ind1.item, dna2: ind2.item, dnaSize: DNA_SIZE)
Since we know the fittest string, I've added a `break` to kill the program if we find it. At the end of a loop at a print statement for the fittest string:
Now we can run the program! Playgrounds are a nice place to develop, but are going to run this program **very slow**. I highly suggest running in Terminal: `swift gen.swift`. When running you should see something like this and it should not take too long to get `Hello, World`:
261
+
262
+
```text
263
+
0: RXclh F HDko
264
+
1: DkyssjgElk];
265
+
2: TiM4u) DrKvZ
266
+
3: Dkysu) DrKvZ
267
+
4: -kysu) DrKvZ
268
+
5: Tlwsu) DrKvZ
269
+
6: Tlwsu) Drd}k
270
+
7: Tlwsu) Drd}k
271
+
8: Tlwsu) Drd}k
272
+
9: Tlwsu) Drd}k
273
+
10: G^csu) |zd}k
274
+
11: G^csu) |zdko
275
+
12: G^csu) |zdko
276
+
13: Dkysu) Drd}k
277
+
14: G^wsu) `rd}k
278
+
15: Dkysu) `rdko
279
+
16: Dkysu) `rdko
280
+
17: Glwsu) `rdko
281
+
18: TXysu) `rdkc
282
+
19: U^wsu) `rdko
283
+
20: G^wsu) `rdko
284
+
21: Glysu) `rdko
285
+
22: G^ysu) `rdko
286
+
23: G^ysu) `ryko
287
+
24: G^wsu) `rdko
288
+
25: G^wsu) `rdko
289
+
26: G^wsu) `rdko
290
+
...
291
+
1408: Hello, Wormd
292
+
1409: Hello, Wormd
293
+
1410: Hello, Wormd
294
+
1411: Hello, Wormd
295
+
1412: Hello, Wormd
296
+
1413: Hello, Wormd
297
+
1414: Hello, Wormd
298
+
1415: Hello, Wormd
299
+
1416: Hello, Wormd
300
+
1417: Hello, Wormd
301
+
1418: Hello, Wormd
302
+
1419: Hello, Wormd
303
+
1420: Hello, Wormd
304
+
1421: Hello, Wormd
305
+
1422: Hello, Wormd
306
+
1423: Hello, Wormd
307
+
1424: Hello, Wormd
308
+
1425: Hello, Wormd
309
+
1426: Hello, Wormd
310
+
1427: Hello, Wormd
311
+
1428: Hello, Wormd
312
+
1429: Hello, Wormd
313
+
1430: Hello, Wormd
314
+
1431: Hello, Wormd
315
+
1432: Hello, Wormd
316
+
1433: Hello, Wormd
317
+
1434: Hello, Wormd
318
+
1435: Hello, Wormd
319
+
fittest string: Hello, World
238
320
```
321
+
322
+
How long it takes will vary since this is based on randomization, but it should almost always finish in under 5000 generations. Woo!
323
+
324
+
325
+
## Now What?
326
+
327
+
We did it, we have a running simple genetic algorithm. Take some time a play around with the global variables, `POP_SIZE`, `OPTIMAL`, `MUTATION_CHANCE`, `GENERATIONS`. Just make sure to only add characters that are in the lexicon, but go ahead and update too!
328
+
329
+
For an example let's try something much longer: `Ray Wenderlich's Swift Algorithm Club Rocks`. Plug that string into `OPTIMAL` and change `GENERATIONS` to `10000`. You'll be able to see that the we are getting somewhere, but you most likely will not reach the optimal string in 10,000 generations. Since we have a larger string let's raise our mutation chance to `200` (1/2 as likely to mutate). You may not get there, but you should get a lot closer than before. With a longer string, too much mutate can make it hard for fit strings to survive. Now try either upping `POP_SIZE` or increase `GENERATIONS`. Either way you should eventually get the value, but there will be a "sweet spot" for an individual of a certain size.
330
+
331
+
Please submit any kind of update to this tutorial or add more examples!
0 commit comments