1- //: Playground - noun: a place where people can play
21
3- import Foundation
42
53extension String {
6- var unicodeArray : [ UInt8 ] {
7- return [ UInt8] ( self . utf8)
8- }
4+
5+ var unicodeArray : [ UInt8 ] {
6+ return [ UInt8] ( self . utf8)
7+ }
98}
109
11-
1210let lex : [ UInt8 ] = " ! \" #$%& \' ()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[ \\ ]^_`abcdefghijklmnopqrstuvwxyz{|}~ " . unicodeArray
1311
1412// 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
1715// The length of the string in our population. Organisms need to be similar
1816let DNA_SIZE = OPTIMAL . count
1917
20- // size of each generation
18+ // Size of each generation
2119let POP_SIZE = 50
2220
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
2422let GENERATIONS = 5000
2523
2624// The chance in which a random nucleotide can mutate (1/n)
2725let MUTATION_CHANCE = 100
2826
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-
3527func 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)
4635 }
47- return pop
36+ pop. append ( dna)
37+ }
38+ return pop
4839}
4940
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
5647}
5748
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
6957 }
70- return items [ 1 ]
58+ n = n - item. weight
59+ }
60+ return items [ 1 ]
7161}
7262
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 ( ) !
8169 }
82-
83- return outputDna
70+ }
71+
72+ return outputDna
8473}
8574
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) )
9482}
9583
84+ var population : [ [ UInt8 ] ] = randomPopulation ( from: lex, populationSize: POP_SIZE, dnaSize: DNA_SIZE)
85+ var fittest = population [ 0 ]
86+ import Foundation
87+
9688func 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+ }
97107
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)
102110
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+ }
144117 }
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) !) " )
146123}
147-
148124main ( )
0 commit comments