to adapt to their environment over time Natural selection A natural process that causes populations of organisms to adapt to their environment over time
to pollution and many trees became blackened by soot • This gave the dark-colored moths an advantage in hiding from predators • By the end of the century, almost all peppered moths were of the dark variety • After the Clean Air Act 1956, and the dark colored moths became rare again
is struggling for survival • DNA Carries genetic information for the organism • Population A group of organisms with different genes (values) for their DNA • Fitness A measurement of how well adapted an organism to its environment
have higher chances to reproduce • Reproduction The next generation of the population is reproduced from the selected best-fit organisms • Inheritance The next generation must inherit the values of the genes • Mutation With each generation, there is a small chance that the values of the genes changes
organisms Select organisms with best fitness Let selected organisms reproduce next generation Next generation inherits from previous generation Randomly mutate each generation Reproduce until goal achieved! The genetic algorithm
of randomly typing the exact sequence out? • 1/26 x 1/26 x 1/26 … 1/26 (1/26 ^ 18) • 1 out of 29,479,510,200,013,900,000,000,000 (29 billion trillion) • If the monkey type a letter every second, there is just 1 chance out of 934,789,136,225,707,600 years that it will type out that quote • That’s 1 time in 934 trillion years! 18 characters
of each organism • The fitness of the organism is how closely it matches the phrase “to be or not to be”, byte by byte • 0 means totally different, 1 means a total match func (d *Organism) calcFitness(target []byte) { score := 0 for i := 0; i < len(d.DNA); i++ { if d.DNA[i] == target[i] { score++ } } d.Fitness = float64(score) / float64(len(d.DNA)) return }
higher chances to reproduce • Create a breeding pool and place a number of copies of the same organism according to its fitness into the pool • The higher the fitness of the organism, the more copies of the organism end up in the pool • This ensures the fittest organisms have higher chances of being picked to pass on the DNA to the next generation
float64) (pool []Organism) { pool = make([]Organism, 0) // create a pool for next generation for i := 0; i < len(population); i++ { population[i].calcFitness(target) num := int((population[i].Fitness / maxFitness) * 100) for n := 0; n < num; n++ { pool = append(pool, population[i]) } } return }
best-fit organisms • Randomly pick the 2 parents from the breeding pool to create the next generation func naturalSelection(pool []Organism, population []Organism, target []byte) []Organism { next := make([]Organism, len(population)) for i := 0; i < len(population); i++ { r1, r2 := rand.Intn(len(pool)), rand.Intn(len(pool)) a := pool[r1] b := pool[r2] child := crossover(a, b) child.mutate() child.calcFitness(target) next[i] = child } return next }
of the genes • Child is bred from the the crossover between 2 randomly picked organisms, and inherits the DNA from both func crossover(d1 Organism, d2 Organism) Organism { child := Organism{ DNA: make([]byte, len(d1.DNA)), Fitness: 0, } mid := rand.Intn(len(d1.DNA)) for i := 0; i < len(d1.DNA); i++ { if i > mid { child.DNA[i] = d1.DNA[i] } else { child.DNA[i] = d2.DNA[i] } } return child }
g p r f s + Randomly select 2 parents from the breeding pool 1 f c e b s r f s Combine the 2 DNA fragments to form the child 3 Randomly select a mid-point in the DNA 2
DNA within the population will always remain the same as the original population • This means if the original population doesn’t have a particular gene that is needed, the optimal result will never be achieved • For example, if the letter t is not found in the initial population at all, we will never be able to come up with the quote no matter how many generations we go through func (d *Organism) mutate() { for i := 0; i < len(d.DNA); i++ { if rand.Float64() < MutationRate { d.DNA[i] = byte(rand.Intn(95) + 32) } } }
pixels, in R, G, B, A order. Pix []uint8 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. Stride int // Rect is the image's bounds. Rect Rectangle } This is our byte array
the organism is the difference between the image and the image of Mona Lisa • The lower the difference, the fitter the organism is • To find the difference, we use the Pythagorean theorem
simply do the Pythagorean theorem twice, and in 4 dimensional space, we do it 3 times • The RGBA values of a pixel is essentially a point in a 4 dimensional space, so to find the difference between 2 pixels !"## = (&2 − &1)++ (-2 − -1)++(.2 − .1)++(/2 − /1)++ … + (.2 − .1)++(/2 − /1)+ • But since Pix is essentially a byte array sequence of RGBA: !"## = (&2 − &1)++ (-2 − -1)++(.2 − .1)++(/2 − /1)+ pixel 1
between 2 images func diff(a, b *image.RGBA) (d int64) { d = 0 for i := 0; i < len(a.Pix); i++ { d += int64(squareDifference(a.Pix[i], b.Pix[i])) } return int64(math.Sqrt(float64(d))) } // square the difference func squareDifference(x, y uint8) uint64 { d := uint64(x) - uint64(y) return d * d }
higher chances to reproduce • Still using the breeding pool but with a slight difference • Instead of using the fitness directly to determine number of copies of organism in the pool, we use a differentiated fitness by subtracting the top organism’s fitness from the least fit organism • For example, if the difference between the best fit organism and the least fit organism in the top best is 20, we place 20 organisms in the breeding pool • If there is no difference between the top best fit organisms, we can’t really create a proper breeding pool. To overcome this, we set the pool to be the whole population
[]Organism) { pool = make([]Organism, 0) sort.SliceStable(population, func(i, j int) bool { return population[i].Fitness < population[j].Fitness }) top := population[0 : PoolSize+1] if top[len(top)-1].Fitness-top[0].Fitness == 0 { pool = population return } for i := 0; i < len(top)-1; i++ { num := (top[PoolSize].Fitness - top[i].Fitness) for n := int64(0); n < num; n++ { pool = append(pool, top[i]) } } return }