1
- //: Playground - noun: a place where people can play
2
1
3
- import Foundation
4
2
5
3
extension String {
6
- var unicodeArray : [ UInt8 ] {
7
- return [ UInt8] ( self . utf8)
8
- }
4
+
5
+ var unicodeArray : [ UInt8 ] {
6
+ return [ UInt8] ( self . utf8)
7
+ }
9
8
}
10
9
11
-
12
10
let lex : [ UInt8 ] = " ! \" #$%& \' ()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[ \\ ]^_`abcdefghijklmnopqrstuvwxyz{|}~ " . unicodeArray
13
11
14
12
// This is the end goal and what we will be using to rate fitness. In the real world this will not exist
@@ -17,132 +15,110 @@ let OPTIMAL:[UInt8] = "Hello, World".unicodeArray
17
15
// The length of the string in our population. Organisms need to be similar
18
16
let DNA_SIZE = OPTIMAL . count
19
17
20
- // size of each generation
18
+ // Size of each generation
21
19
let POP_SIZE = 50
22
20
23
- // max number of generations, script will stop when it reach 5000 if the optimal value is not found
21
+ // Max number of generations, script will stop when it reaches 5000 if the optimal value is not found
24
22
let GENERATIONS = 5000
25
23
26
24
// The chance in which a random nucleotide can mutate (1/n)
27
25
let MUTATION_CHANCE = 100
28
26
29
- func randomChar( from lexicon: [ UInt8 ] ) -> UInt8 {
30
- let len = UInt32 ( lexicon. count- 1 )
31
- let rand = Int ( arc4random_uniform ( len) )
32
- return lexicon [ rand]
33
- }
34
-
35
27
func randomPopulation( from lexicon: [ UInt8 ] , populationSize: Int , dnaSize: Int ) -> [ [ UInt8 ] ] {
36
-
37
- var pop = [ [ UInt8] ] ( )
38
-
39
- ( 0 ..< populationSize) . forEach { _ in
40
- var dna = [ UInt8] ( )
41
- ( 0 ..< dnaSize) . forEach { _ in
42
- let char = randomChar ( from: lexicon)
43
- dna. append ( char)
44
- }
45
- pop. append ( dna)
28
+ var pop = [ [ UInt8] ] ( )
29
+
30
+ ( 0 ..< populationSize) . forEach { _ in
31
+ var dna = [ UInt8] ( )
32
+ ( 0 ..< dnaSize) . forEach { _ in
33
+ let char = lexicon. randomElement ( ) !
34
+ dna. append ( char)
46
35
}
47
- return pop
36
+ pop. append ( dna)
37
+ }
38
+ return pop
48
39
}
49
40
50
- func calculateFitness( dna: [ UInt8 ] , optimal: [ UInt8 ] ) -> Int {
51
- var fitness = 0
52
- ( 0 ... dna. count - 1 ) . forEach { c in
53
- fitness += abs ( Int ( dna [ c ] ) - Int( optimal [ c ] ) )
54
- }
55
- return fitness
41
+ func calculateFitness( dna: [ UInt8 ] , optimal: [ UInt8 ] ) -> Int {
42
+ var fitness = 0
43
+ for index in dna. indices {
44
+ fitness += abs ( Int ( dna [ index ] ) - Int( optimal [ index ] ) )
45
+ }
46
+ return fitness
56
47
}
57
48
58
- func weightedChoice( items: [ ( dna: [ UInt8 ] , weight: Double ) ] ) -> ( dna: [ UInt8 ] , weight: Double ) {
59
-
60
- let total = items. reduce ( 0.0 ) { return $0 + $1. weight}
61
-
62
- var n = Double ( arc4random_uniform ( UInt32 ( total * 1000000.0 ) ) ) / 1000000.0
63
-
64
- for item in items {
65
- if n < item. weight {
66
- return item
67
- }
68
- n = n - item. weight
49
+ func weightedChoice( items: [ ( dna: [ UInt8 ] , weight: Double ) ] ) -> ( dna: [ UInt8 ] , weight: Double ) {
50
+
51
+ let total = items. reduce ( 0 ) { $0 + $1. weight }
52
+ var n = Double . random ( in: 0 ..< ( total * 1000000 ) ) / 1000000.0
53
+
54
+ for item in items {
55
+ if n < item. weight {
56
+ return item
69
57
}
70
- return items [ 1 ]
58
+ n = n - item. weight
59
+ }
60
+ return items [ 1 ]
71
61
}
72
62
73
- func mutate( lexicon: [ UInt8 ] , dna: [ UInt8 ] , mutationChance: Int ) -> [ UInt8 ] {
74
- var outputDna = dna
75
-
76
- ( 0 ..< dna. count) . forEach { i in
77
- let rand = Int ( arc4random_uniform ( UInt32 ( mutationChance) ) )
78
- if rand == 1 {
79
- outputDna [ i] = randomChar ( from: lexicon)
80
- }
63
+ func mutate( lexicon: [ UInt8 ] , dna: [ UInt8 ] , mutationChance: Int ) -> [ UInt8 ] {
64
+ var outputDna = dna
65
+ ( 0 ..< dna. count) . forEach { i in
66
+ let rand = Int . random ( in: 0 ..< mutationChance)
67
+ if rand == 1 {
68
+ outputDna [ i] = lexicon. randomElement ( ) !
81
69
}
82
-
83
- return outputDna
70
+ }
71
+
72
+ return outputDna
84
73
}
85
74
86
-
87
- func crossover( dna1: [ UInt8 ] , dna2: [ UInt8 ] , dnaSize: Int ) -> [ UInt8 ] {
88
- let pos = Int ( arc4random_uniform ( UInt32 ( dnaSize- 1 ) ) )
89
-
90
- let dna1Index1 = dna1. index ( dna1. startIndex, offsetBy: pos)
91
- let dna2Index1 = dna2. index ( dna2. startIndex, offsetBy: pos)
92
-
93
- return [ UInt8] ( dna1. prefix ( upTo: dna1Index1) + dna2. suffix ( from: dna2Index1) )
75
+ func crossover( dna1: [ UInt8 ] , dna2: [ UInt8 ] , dnaSize: Int ) -> [ UInt8 ] {
76
+ let pos = Int . random ( in: 0 ..< dnaSize)
77
+
78
+ let dna1Index1 = dna1. index ( dna1. startIndex, offsetBy: pos)
79
+ let dna2Index1 = dna2. index ( dna2. startIndex, offsetBy: pos)
80
+
81
+ return [ UInt8] ( dna1. prefix ( upTo: dna1Index1) + dna2. suffix ( from: dna2Index1) )
94
82
}
95
83
84
+ var population : [ [ UInt8 ] ] = randomPopulation ( from: lex, populationSize: POP_SIZE, dnaSize: DNA_SIZE)
85
+ var fittest = population [ 0 ]
86
+ import Foundation
87
+
96
88
func main( ) {
89
+ for generation in 0 ... GENERATIONS {
90
+ var weightedPopulation = [ ( dna: [ UInt8] , weight: Double) ] ( )
91
+
92
+ for individual in population {
93
+ let fitnessValue = calculateFitness ( dna: individual, optimal: OPTIMAL)
94
+ let pair = ( individual, fitnessValue == 0 ? 1.0 : 1.0 / Double( fitnessValue) )
95
+ weightedPopulation. append ( pair)
96
+ }
97
+
98
+ population = [ ]
99
+
100
+ ( 0 ... POP_SIZE) . forEach { _ in
101
+ let ind1 = weightedChoice ( items: weightedPopulation)
102
+ let ind2 = weightedChoice ( items: weightedPopulation)
103
+
104
+ let offspring = crossover ( dna1: ind1. dna, dna2: ind2. dna, dnaSize: DNA_SIZE)
105
+ population. append ( mutate ( lexicon: lex, dna: offspring, mutationChance: MUTATION_CHANCE) )
106
+ }
97
107
98
- // generate the starting random population
99
- var population : [ [ UInt8 ] ] = randomPopulation ( from: lex, populationSize: POP_SIZE, dnaSize: DNA_SIZE)
100
- // print("population: \(population), dnaSize: \(DNA_SIZE) ")
101
- var fittest = [ UInt8] ( )
108
+ fittest = population [ 0 ]
109
+ var minFitness = calculateFitness ( dna: fittest, optimal: OPTIMAL)
102
110
103
- for generation in 0 ... GENERATIONS {
104
-
105
- var weightedPopulation = [ ( dna: [ UInt8] , weight: Double) ] ( )
106
-
107
- // calulcated the fitness of each individual in the population
108
- // and add it to the weight population (weighted = 1.0/fitness)
109
- for individual in population {
110
- let fitnessValue = calculateFitness ( dna: individual, optimal: OPTIMAL)
111
-
112
- let pair = ( individual, fitnessValue == 0 ? 1.0 : Double ( 100 / POP_SIZE) / Double( fitnessValue ) )
113
-
114
- weightedPopulation. append ( pair)
115
- }
116
-
117
- population = [ ]
118
-
119
- // create a new generation using the individuals in the origional population
120
- ( 0 ... POP_SIZE) . forEach { _ in
121
- let ind1 = weightedChoice ( items: weightedPopulation)
122
- let ind2 = weightedChoice ( items: weightedPopulation)
123
-
124
- let offspring = crossover ( dna1: ind1. dna, dna2: ind2. dna, dnaSize: DNA_SIZE)
125
-
126
- // append to the population and mutate
127
- population. append ( mutate ( lexicon: lex, dna: offspring, mutationChance: MUTATION_CHANCE) )
128
- }
129
-
130
- fittest = population [ 0 ]
131
- var minFitness = calculateFitness ( dna: fittest, optimal: OPTIMAL)
132
-
133
- // parse the population for the fittest string
134
- population. forEach { indv in
135
- let indvFitness = calculateFitness ( dna: indv, optimal: OPTIMAL)
136
- if indvFitness < minFitness {
137
- fittest = indv
138
- minFitness = indvFitness
139
- }
140
- }
141
- if minFitness == 0 { break ; }
142
- print ( " \( generation) : \( String ( bytes: fittest, encoding: . utf8) !) " )
143
-
111
+ population. forEach { indv in
112
+ let indvFitness = calculateFitness ( dna: indv, optimal: OPTIMAL)
113
+ if indvFitness < minFitness {
114
+ fittest = indv
115
+ minFitness = indvFitness
116
+ }
144
117
}
145
- print ( " fittest string: \( String ( bytes: fittest, encoding: . utf8) !) " )
118
+
119
+ if minFitness == 0 { break }
120
+ print ( " \( generation) : \( String ( bytes: fittest, encoding: . utf8) !) " )
121
+ }
122
+ print ( " fittest string: \( String ( bytes: fittest, encoding: . utf8) !) " )
146
123
}
147
-
148
124
main ( )
0 commit comments