package main
import (
"errors"
"fmt"
"math/rand"
"os"
"sort"
"strconv"
"time"
"unicode/utf8"
)
type populationItem struct {
Key string
Value float64
}
func geneticString(target string, charmap []rune) (int, int, string) {
populationNum := 200
selectionNum := 50
mutationProb := .4
rand.Seed(time.Now().UnixNano())
if populationNum < selectionNum {
fmt.Println(errors.New("PopulationNum must be bigger tha selectionNum "))
os.Exit(1)
}
for position, r := range []rune(target) {
find := func() bool {
for _, n := range charmap {
if n == r {
return true
}
}
return false
}
if !find() {
fmt.Println(errors.New("Character not aviable in charmap"), position, "\"", string(r), "\"")
os.Exit(1)
}
}
pop := make([]populationItem, populationNum, populationNum)
for i := 0; i < populationNum; i++ {
key := ""
for x := 0; x < utf8.RuneCountInString(target); x++ {
choice := rand.Intn(len(charmap))
key += string(charmap[choice])
}
pop[i] = populationItem{key, 0}
}
gen, generatedPop := 0, 0
for {
gen++
generatedPop += len(pop)
for i, item := range pop {
pop[i].Value = 0
itemKey, targetRune := []rune(item.Key), []rune(target)
for x := 0; x < len(target); x++ {
if itemKey[x] == targetRune[x] {
pop[i].Value++
}
}
pop[i].Value = pop[i].Value / float64(len(targetRune))
}
sort.SliceStable(pop, func(i, j int) bool { return pop[i].Value > pop[j].Value })
if pop[0].Key == target {
break
}
if gen%10 == 0 {
fmt.Println("Generation:", strconv.Itoa(gen), "Analyzed:", generatedPop, "Best:", pop[0])
}
var popChildren []populationItem
popChildren = append(popChildren, pop[0:int(selectionNum/3)]...)
for i := 0; i < int(selectionNum); i++ {
parent1 := pop[i]
nChild := (parent1.Value * 100) + 1
if nChild >= 10 {
nChild = 10
}
for x := 0.0; x < nChild; x++ {
parent2 := pop[rand.Intn(selectionNum)]
split := rand.Intn(utf8.RuneCountInString(target))
child1 := append([]rune(parent1.Key)[:split], []rune(parent2.Key)[split:]...)
child2 := append([]rune(parent2.Key)[:split], []rune(parent1.Key)[split:]...)
if rand.Float64() < mutationProb {
child1[rand.Intn(len(child1))] = charmap[rand.Intn(len(charmap))]
}
if rand.Float64() < mutationProb {
child2[rand.Intn(len(child2))] = charmap[rand.Intn(len(charmap))]
}
popChildren = append(popChildren, populationItem{string(child1), 0})
popChildren = append(popChildren, populationItem{string(child2), 0})
if len(popChildren) >= selectionNum {
break
}
}
}
pop = popChildren
}
return gen, generatedPop, pop[0].Key
}
func main() {
target := string("This is a genetic algorithm to evaluate, combine, evolve and mutate a string!")
charmap := []rune(" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.,;!?+-*#@^'èéòà€ù=)(&%$£/\\")
gen, generatedPop, best := geneticString(target, charmap)
fmt.Println("Generation:", strconv.Itoa(gen), "Analyzed:", generatedPop, "Best:", best)
}