Skip to content

Commit a1e2e9d

Browse files
authored
Updates code to Swift 4.2
1 parent de935b7 commit a1e2e9d

File tree

1 file changed

+84
-108
lines changed

1 file changed

+84
-108
lines changed

Genetic/gen.playground/Contents.swift

+84-108
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
//: Playground - noun: a place where people can play
21

3-
import Foundation
42

53
extension 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-
1210
let 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
1816
let DNA_SIZE = OPTIMAL.count
1917

20-
// size of each generation
18+
// Size of each generation
2119
let 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
2422
let GENERATIONS = 5000
2523

2624
// The chance in which a random nucleotide can mutate (1/n)
2725
let 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-
3527
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)
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+
9688
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+
}
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-
148124
main()

0 commit comments

Comments
 (0)