Quantcast
Channel: Hacker News
Viewing all articles
Browse latest Browse all 25817

A Very Simple Genetic Algorithm Written in Swift 3

$
0
0
#!/usr/bin/env xcrun swift/*gen.swift is a direct port of cfdrake's helloevolve.py from Python 2.7 to Swift 3-------------------- https://gist.github.com/cfdrake/973505 ---------------------gen.swift implements a genetic algorithm that starts with a basepopulation of randomly generated strings, iterates over a certain number ofgenerations while implementing 'natural selection', and prints out the most fitstring.The parameters of the simulation can be changed by modifying one of the manyglobal variables. To change the "most fit" string, modify OPTIMAL. POP_SIZEcontrols the size of each generation, and GENERATIONS is the amount ofgenerations that the simulation will loop through before returning the fitteststring.This program subject to the terms of The MIT License listed below.----------------------------------------------------------------------------------Copyright (c) 2016 Blaine RothrockPermission is hereby granted, free of charge, to any person obtaining a copy ofthis software and associated documentation files (the "Software"), to deal in theSoftware without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subjectto the following conditions:The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR APARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHTHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."""*/importFoundationlet OPTIMAL ="Hello, World"let DNA_SIZE = OPTIMAL.characters.countlet POP_SIZE =50let GENERATIONS =5000let MUTATION_CHANCE =100// HELPERS/* String extension to convert a string to ascii value*/extensionString {var asciiArray: [UInt32] {return unicodeScalars.filter{$0.isASCII}.map{$0.value} }}/* extenstion to convert a character to ascii value*/extensionCharacter {var asciiValue:UInt32? {returnString(self).unicodeScalars.filter{$0.isASCII}.first?.value }}/* helper function to return a random character string*/funcrandomChar() ->String {let letters : NSString ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ,."let len =UInt32(letters.length-1)let rand =arc4random_uniform(len)returnString(Character(UnicodeScalar(letters.character(at: Int(rand)))!))// NOTE: above solution is faster//return String(letters[letters.index(letters.startIndex, offsetBy: Int(rand))])}// END HELPERS/* calculated the fitness based on approximate string matching compares each character ascii value difference and adds that to a total fitness optimal string comparsion = 0*/funccalculateFitness(dna:String, optimal:String) ->Int {var fitness =0let dnaLetters = dna.characters.map { String($0) }let optimalLetters = optimal.characters.map { String($0) }for c in0...dna.characters.count-1 { fitness +=abs(Int(Character(dnaLetters[c]).asciiValue!) -Int(Character(optimalLetters[c]).asciiValue!)) }return fitness}/* randomly mutate the string*/funcmutate(dna:String, mutationChance:Int, dnaSize:Int) ->String {var outputDna =""let dnaLetters = dna.characters.map { returnString($0) }for i in0..<dnaSize {let rand =Int(arc4random_uniform(UInt32(mutationChance)))if rand ==1 {let ranchar =String(randomChar())! outputDna += ranchar } else { outputDna += dnaLetters[i] } }return outputDna}/* combine two parents to create an offspring parent = xy & yx, offspring = xx, yy*/funccrossover(dna1:String, dna2:String, dnaSize:Int) -> (dna1:String, dna2:String) {let pos =Int(arc4random_uniform(UInt32(dnaSize-1)))let dna1Index1 = dna1.index(dna1.startIndex, offsetBy: pos)let dna2Index1 = dna2.index(dna2.startIndex, offsetBy: pos)return (dna1.substring(to: dna1Index1) + dna2.substring(from: dna2Index1), dna2.substring(to: dna2Index1) + dna1.substring(from: dna1Index1))}/*returns a random population, used to start the evolution*/funcrandomPopulation(populationSize: Int, dnaSize: Int) -> [String] {let letters : NSString ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ,."let len =UInt32(letters.length)var randomString =""var pop = [String]()for_in0..<populationSize {var dna =""for_in0..<dnaSize {let rand =arc4random_uniform(len)var nextChar = letters.character(at: Int(rand)) dna +=NSString(characters: &nextChar, length: 1) asString } pop.append(dna) }return pop}/*function to return random canidate of a population randomally, but weight on fitness.*/funcweightedChoice(items:[(item:String, weight:Double)]) -> (item:String, weight:Double) {var weightTotal =0.0for itemTuple in items { weightTotal += itemTuple.weight; }var n =Double(arc4random_uniform(UInt32(weightTotal *1000000.0))) /1000000.0for itemTuple in items {if n < itemTuple.weight {return itemTuple } n = n - itemTuple.weight }return items[1]}funcmain() {// generate the starting random populationvar population:[String] =randomPopulation(populationSize: POP_SIZE, dnaSize: DNA_SIZE)// print("population: \(population), dnaSize: \(DNA_SIZE) ")var fittestString =""for generation in0...GENERATIONS {print("Generation \(generation) with random sample: \(population[0])")var weightedPopulation = [(item:String, weight:Double)]()// calulcated the fitness of each individual in the population// and add it to the weight population (weighted = 1.0/fitness)for individual in population {let fitnessValue =calculateFitness(dna: individual, optimal: OPTIMAL)var pair = ("",0.0)if fitnessValue ==0 { pair = (individual, 1.0) } else { pair = (individual, 1.0/Double(fitnessValue)) } weightedPopulation.append(pair) } population = []// create a new generation using the individuals in the origional populationfor i in0...POP_SIZE/2 {let ind1 =weightedChoice(items: weightedPopulation)let ind2 =weightedChoice(items: weightedPopulation)var offspring =crossover(dna1: ind1.item, dna2: ind2.item, dnaSize: DNA_SIZE)// append to the population and mutate population.append(mutate(dna: offspring.dna1, mutationChance: MUTATION_CHANCE, dnaSize: DNA_SIZE)) population.append(mutate(dna: offspring.dna2, mutationChance: MUTATION_CHANCE, dnaSize: DNA_SIZE)) }let fitnessString = population[0]var minFitness =calculateFitness(dna: fitnessString, optimal: OPTIMAL)// parse the population for the fittest stringfor indv in population {let indvFitness =calculateFitness(dna: indv, optimal: OPTIMAL)if indvFitness < minFitness { fittestString = indv minFitness = indvFitness } } }print("fittest string: \(fittestString)")}main()

Viewing all articles
Browse latest Browse all 25817

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>