|
| 1 | +package jurisk.optimization |
| 2 | + |
| 3 | +import scala.util.Random |
| 4 | + |
| 5 | +object GeneticAlgorithm { |
| 6 | + private val Debug = false |
| 7 | + private type Population[T] = Vector[T] |
| 8 | + |
| 9 | + def geneticAlgorithm[Chromosome]( |
| 10 | + populationSize: Int, |
| 11 | + fitnessFunction: Chromosome => Double, |
| 12 | + crossover: (Chromosome, Chromosome) => (Chromosome, Chromosome), |
| 13 | + mutate: Chromosome => Chromosome, |
| 14 | + randomChromosome: () => Chromosome, |
| 15 | + generations: Int, |
| 16 | + ): Chromosome = { |
| 17 | + def evolve(population: Population[Chromosome]): Population[Chromosome] = { |
| 18 | + val fitnessScores = population map fitnessFunction |
| 19 | + val bestIndex = fitnessScores.indexOf(fitnessScores.max) |
| 20 | + val best = population(bestIndex) |
| 21 | + if (Debug) { |
| 22 | + println(s"Best: $best with fitness ${fitnessScores(bestIndex)}") |
| 23 | + } |
| 24 | + |
| 25 | + Vector |
| 26 | + .fill(populationSize / 2) { |
| 27 | + val parent1 = selection(population, fitnessScores) |
| 28 | + val parent2 = selection(population, fitnessScores) |
| 29 | + val (child1, child2) = crossover(parent1, parent2) |
| 30 | + Vector(mutate(child1), mutate(child2)) |
| 31 | + } |
| 32 | + .flatten |
| 33 | + } |
| 34 | + |
| 35 | + def selection( |
| 36 | + population: Population[Chromosome], |
| 37 | + fitnessScores: Vector[Double], |
| 38 | + ): Chromosome = { |
| 39 | + val minFitness = fitnessScores.min |
| 40 | + val normalizedScores = fitnessScores map (_ - minFitness) |
| 41 | + val totalFitness = normalizedScores.sum |
| 42 | + val selectedValue = Random.nextDouble() * totalFitness |
| 43 | + val cumulativeFitness = normalizedScores.scanLeft(0.0)(_ + _).tail |
| 44 | + val selectedIndex = cumulativeFitness.indexWhere(_ >= selectedValue) |
| 45 | + population(selectedIndex) |
| 46 | + } |
| 47 | + |
| 48 | + val initialPopulation = Vector.fill(populationSize)(randomChromosome()) |
| 49 | + val finalPopulation = (1 to generations).foldLeft(initialPopulation) { |
| 50 | + (pop, idx) => |
| 51 | + if (Debug) { |
| 52 | + println(s"Generation $idx") |
| 53 | + } |
| 54 | + evolve(pop) |
| 55 | + } |
| 56 | + |
| 57 | + finalPopulation.maxBy(fitnessFunction) |
| 58 | + } |
| 59 | +} |
0 commit comments