How does a genetic algorithm work?
Updated Feb 20, 2026
Short answer
A genetic algorithm evolves a population of candidate solutions towards better ones by mimicking natural selection. Each generation scores every candidate with a fitness function, selects the fitter ones to reproduce, combines pairs via crossover, applies random mutation, and repeats. It is a stochastic search for problems where the space is too large to enumerate and no gradient is available.
Deep explanation
def genetic_algorithm(fitness, population_size, generations): population = [random_individual() for _ in range(population_size)] for _ in range(generations): scored = sorted(population, key=fitness, reverse=True) elite = scored[:2] # ELITISM: keep the best unchanged children = [] while len(children) < population_size - len(elite): p1, p2 = tournament_select(scored, fitness), tournament_select(scored, fitness) child = crossover(p1, p2) if random.random() < 0.02: # low mutation rate child = mutate(child) children.append(child) population = elite + children return max(population, key=fitness)The five components, and what each contributes:
- Representation — how a solution is encoded. This is the most consequential design decision; a poor encoding makes crossover produce nonsense.
- Fitness function — scores a candidate. Called constantly, so its cost dominates runtime.
- Selection — biases reproduction towards fitter individuals. Tournament selection (pick k at random, keep the best) is usual because it is cheap and its selection pressure is tunable via k.
- Crossover — combines two parents, the exploitation mechanism, recombining building blocks that already work.
- Mutation — random perturbation, the exploration mechanism, reintroducing lost diversity.
The central tension is exploration against exploitation. Too much selection pressure and the population converges prematurely on a local optimum, all individuals nearly identical and crossover producing nothing new. Too much mutation and it degenerates into random search. Mutation rates are typically 0.1–5%.
Elitism matters more than it looks. Without carrying the best individuals forward unchanged, a good solution can be lost to unlucky crossover, and fitness can go down between generations.
When a GA is the right tool: the search space is huge and discrete, the fitness landscape is rugged with many local optima, there is no gradient to follow, and a good-enough answer is acceptable. Scheduling, routing, circuit layout, hyperparameter search, and neural architecture search all fit.
When it is not: if the problem is convex or differentiable, gradient descent is far faster. If an exact algorithm exists — shortest path, linear programming — use it. GAs guarantee nothing about optimality and require substantial tuning.
Real-world example
Scheduling exams to minimise conflicts:
# representation: a list where index = exam, value = time slotindividual = [2, 0, 1, 2, 3, 0] # exam 0 in slot 2, exam 1 in slot 0, ...
def fitness(schedule): conflicts = sum(1 for a, b in student_pairs() if schedule[a] == schedule[b]) # same student, same slot slots_used = len(set(schedule)) return -(conflicts * 100 + slots_used) # conflicts dominateCrossover takes slot assignments from both parents, mutation reassigns one exam to a random slot. With 200 exams and 20 slots the space is 20²⁰⁰ — no enumeration is possible, and there is no gradient to descend, but a GA reliably finds a workable timetable within a few thousand generations.
The weighting in the fitness function is doing real work: conflicts cost 100× a slot, so the search prioritises validity over compactness.
Common mistakes
- - Choosing a representation where crossover produces invalid individuals, then repairing them constantly — which usually means the encoding is wrong.
- - Setting the mutation rate too high, degrading the search into random sampling and destroying the building blocks crossover assembles.
- - Omitting elitism, so the best solution found can be lost and fitness regresses between generations.
- - Applying excessive selection pressure, causing premature convergence on a local optimum with a population of near-identical clones.
- - Using a GA on a convex or differentiable problem where gradient descent would be orders of magnitude faster.
- - Writing an expensive fitness function without profiling
- it is evaluated population_size × generations times and dominates everything else.
Follow-up questions
- What is premature convergence and how do you counter it?
- Why is elitism important?
- How do you choose a selection method?
- When is simulated annealing preferable to a genetic algorithm?