HomeArticlesGenetic Algorithm

Genetic Algorithms: Evolution as Optimisation

Selection, crossover and mutation turned into a search algorithm — from Dawkins's Weasel to the trap-riddled Rastrigin landscape.

mysimulator teamUpdated June 2026≈ 7 min read▶ Open the simulation

Search dressed as evolution

A genetic algorithm treats candidate solutions to a problem as individuals in a population and improves the population generation by generation using the same three ingredients that drive biological evolution: selection (better solutions are more likely to reproduce), crossover (offspring combine traits from two parents) and mutation (small random changes introduce variation selection alone could never produce). Each candidate is scored by a fitness function specific to the problem — string similarity to a target, or a mathematical function to minimise — and nothing in the algorithm needs a derivative or a differentiable objective, which is exactly why GAs are used on problems where gradient-based methods have nothing to grab onto.

live demo · a population converging generation by generation● LIVE

Weasel: the smallest possible demonstration

Richard Dawkins's 1986 "Weasel" program is the simplest illustrative case: evolve a random string of characters toward a fixed target phrase (his example: METHINKS IT IS LIKE A WEASEL) purely through mutation and selection of the single fittest string each generation, with no crossover at all. Its point was never that this models real biology closely — it targets a known fixed string, which real evolution never does — but to make one thing viscerally clear: cumulative selection of small random changes converges dramatically faster than pure chance. A truly random 28-character string matching the target letter-for-letter has probability roughly (1/27)²⁸ — astronomically unlikely to occur by one lucky draw — yet Weasel typically converges within a few dozen generations because each generation keeps whatever improvement mutation happened to produce, instead of restarting from scratch.

Rastrigin: a fitness landscape built to have many traps

The second mode on this page optimises the 2D Rastrigin function, a standard benchmark in optimisation research precisely because it is hard in a specific, well-understood way:

f(x, y) = 20 + x^2 + y^2 - 10*cos(2*pi*x) - 10*cos(2*pi*y)
// global minimum: f(0, 0) = 0
// but riddled with regularly-spaced local minima nearby

The quadratic term x² + y² creates one smooth global bowl centred at the origin, while the cosine terms punch a regular lattice of local minima into that bowl — a landscape a simple hill-climber gets stuck in almost immediately, because moving toward the nearest downhill direction usually leads into the wrong local dip rather than the true minimum at the origin. A genetic algorithm's population-based search, plus mutation's ability to make a jump larger than one local basin, is specifically what such landscapes are designed to test.

Selection, crossover, mutation — the three operators

Tournament selection picks k individuals at random from the population and lets the fittest of that small group become a parent; it is preferred over always picking the single best individual because it keeps selection pressure tunable (a larger k pushes harder toward the current best, a smaller k preserves more diversity) without ever needing to rank the whole population. Crossover combines two parents' genomes — for the Weasel string, a cut point splits each parent and swaps the tails; for Rastrigin, the (x, y) coordinates can be blended or averaged. Mutation then perturbs the offspring slightly and independently of the parents — a low but non-zero mutation rate is what keeps the population from prematurely collapsing onto one local optimum and losing the ability to explore.

loop each generation:
  evaluate fitness(individual) for every individual
  new_population = []
  while new_population not full:
    parent1 = tournament_select(population)
    parent2 = tournament_select(population)
    child   = crossover(parent1, parent2)
    child   = mutate(child, rate)
    new_population.push(child)
  population = new_population

There is no guarantee of finding the global optimum — genetic algorithms are heuristics, not exact solvers — but on landscapes that are noisy, discontinuous, non-differentiable or simply unknown in closed form, a population that explores broadly and exploits locally tends to outperform both pure random search and gradient descent, which either wanders aimlessly or gets stuck at the first local minimum it meets.

Frequently asked questions

Does a genetic algorithm always find the best possible solution?

No. It is a heuristic search, not an exact algorithm — it can get stuck in a good but non-optimal region of the search space, especially with a small population or too low a mutation rate. What it reliably does is improve steadily over generations without needing a derivative of the fitness function.

Why does the Weasel demo target a fixed known phrase — isn't that unlike real evolution?

Yes, and Dawkins was explicit about that limitation. Real evolution has no target to converge toward, only ongoing selection pressure. Weasel exists purely to demonstrate that cumulative selection of small random changes is vastly faster than pure random chance, not to model biological evolution accurately.

What does the mutation rate actually control?

How much random variation each offspring gets beyond what crossover already combined from its parents. Too low and the population converges quickly but can get permanently stuck on a local optimum; too high and useful traits get destroyed almost as fast as they appear, and the search behaves more like random noise.

Try it live

Everything above runs in your browser — open Genetic Algorithm and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.

▶ Open Genetic Algorithm simulation

What did you find?

Add reproduction steps (optional)