Last active
March 8, 2022 07:18
-
-
Save VizGhar/8736733abc91d7131afbe89ae7b774b6 to your computer and use it in GitHub Desktop.
"Hello World!" Simplistic Genetic Algorithm with tournament selectionThis sample is meant to be readable and is not efficient at all, so!!! Do not use in production !!!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import kotlin.math.abs | |
import kotlin.random.Random | |
const val target = "Hello World!" | |
const val possibilities = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789 .,!?" | |
const val mutationProbability = 0.05 // each block of chromosome mutates with this probability | |
const val populationSize = 1000 // this many individuals will be in each generation | |
const val keepNBest = 200 // this many best individuals will be taken without change to next gen | |
const val tournamentSize = 3 // this many individuals will fight in tournament | |
val random = Random(System.currentTimeMillis()) | |
fun randomIndividual() = (target.indices).map { possibilities.random(random) }.joinToString("") | |
var population = (0 until populationSize).map { randomIndividual() }.sortedBy { it.fitness } | |
val String.fitness: Int get() = // lower fitness better (for this case) fitness of 0 = solution | |
indices.sumOf { index -> | |
val indexFirst = possibilities.indexOf(target[index]) | |
val indexSecond = possibilities.indexOf(this[index]) | |
val high = maxOf(indexFirst, indexSecond) | |
val low = minOf(indexFirst, indexSecond) | |
if ((high - low) > possibilities.length / 2) { | |
abs(high - possibilities.length - low) | |
} else { | |
high - low | |
} | |
} | |
fun mutation(individual: String) = | |
individual.map { if (Math.random() <= mutationProbability) possibilities.random(random) else it }.joinToString("") | |
fun crossover(individual1: String, individual2: String) = | |
random.nextInt(target.length).let { individual1.substring(0, it) + individual2.substring(it) } | |
fun tournament() = | |
(1..tournamentSize).map { population.random(random) }.minByOrNull { it.fitness }!! | |
fun nextPopulation() { | |
val best = (0 until keepNBest).map { population[it] } | |
val kids = (keepNBest until populationSize).map { | |
val individual1 = tournament() | |
val individual2 = tournament() | |
val child = crossover(individual1, individual2) | |
mutation(child) | |
} | |
population = (best + kids).sortedBy { it.fitness } | |
} | |
fun main() { | |
var generation = 0 | |
while (population[0].fitness != 0) { | |
println("Generation $generation : fitness = ${population[0].fitness} : best = ${population[0]}") | |
nextPopulation() | |
generation++ | |
} | |
println("Generation $generation : result : ${population[0]}") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment