Skip to content
Sample of Genetic Algorithm (Evolve Toward Target String)

shiffman_pearson_textbook · geneticAlgorithmString

Genetic Algorithm (Evolve Toward Target String)

A textbook genetic algorithm that evolves a population of binary strings toward a target ASCII string (default 'HELLO WORLD!'). Each character is 8 bits MSB-first, so the gene length is 8 * len(target). Per generation: (1) evaluate fitness = Hamming match count, (2) tournament selection with tournament_size contestants, (3) single-point crossover with prob crossover_rate, (4) per-bit mutation with prob mutation_rate, (5) replace population; elitism preserves the top-N. The canvas shows the current population as a binary matrix (each row = individual, match/miss colour per bit), the best individual decoded above the grid, and a fitness curve (mean + best) at the bottom across the last history_max generations.

Original: https://natureofcode.com/evolutionary-computing/

▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.

Family
shiffman_pearson_textbook
Source
shiffman · pearson
Evolution
genetic_algorithm
Render
population_grid_with_fitness_curve
Complexity
medium
Agents
80
Year
1975
Author
Holland (1975); Shiffman (Nature of Code Ch. 9 'Evolutionary Computing'); Pearson (Generative Art Ch. 5 'Evolution')

Math essence

Per generation: fit = (pop == target).sum(axis=1); tournament = argmax(fit[randint(0, pop, tournament_size)]); crossover = single-point cut at randint(1, gene_len) with prob crossover_rate; mutate = bit-flip with prob mutation_rate. Elitism carries top-N over. Vectorised over numpy arrays; ASCII encode/decode uses (bit * 2^(7-i)).sum() per byte. Canvas: per-cell rectangle based on pop[r, c] vs target_bits[c]; curve at bottom uses Bresenham-style line stroke.

Parameters

Name Type Default Range Description
dim int 480 200–1200
target str HELLO WORLD! — ASCII target string; each char is 8 bits.
pop_size int 80 10–400
tournament_size int 5 2–20
crossover_rate float 0.7 0–1
mutation_rate float 0.01 0–0.2
elitism int 2 0–20
history_max int 80 10–400
bg_color tuple 15,16,24 —
match_color tuple 220,230,255 —
miss_color tuple 40,36,50 —
curve_color tuple 255,200,90 —
best_color tuple 130,255,170 —
seed int null 0–999999

Reference

First 60 lines of reference.py. Full source: library_external/shiffman_pearson_textbook/geneticAlgorithmString/reference.py

from __future__ import annotations

from typing import ClassVar

import numpy as np


class GeneticAlgorithmString:
    """Genetic algorithm evolving toward a target string.

    A population of ``pop_size`` binary strings of length
    ``gene_len = 8 * len(target)`` (each ASCII char is 8 bits, MSB first)
    evolves under a textbook GA loop (Holland 1975; Mitchell 1996) at
    each ``step(n)`` call:

    1. **Evaluate**: each individual's fitness is the count of bits
       matching the target (Hamming similarity).
    2. **Select**: ``tournament_size``-way tournament with replacement
       picks a parent.
    3. **Crossover**: single-point crossover with probability
       ``crossover_rate``.
    4. **Mutate**: each bit is flipped with probability ``mutation_rate``.
    5. The new population replaces the old (generational GA, elitism
       optional).

    ``step(n)`` runs ``n`` generations. The canvas shows the current
    population as a binary matrix (each row is an individual; white =
    matching bit, dark = mismatching bit) at the top, with a fitness
    curve (mean and best across generations) at the bottom and the
    current best individual decoded as ASCII above.

    Pearson (*Generative Art* Ch. 5 'Evolution') and Shiffman
    (*Nature of Code* Ch. 9 'Evolutionary Computing') both feature this
    exact example as the simplest nontrivial GA.
    """

    family: ClassVar[str] = "shiffman_pearson_textbook"
    sketch: ClassVar[str] = "geneticAlgorithmString"

    def __init__(
        self,
        dim: int = 480,
        target: str = "HELLO WORLD!",
        pop_size: int = 80,
        tournament_size: int = 5,
        crossover_rate: float = 0.7,
        mutation_rate: float = 0.01,
        elitism: int = 2,
        history_max: int = 80,
        bg_color: tuple[int, int, int] = (15, 16, 24),
        match_color: tuple[int, int, int] = (220, 230, 255),
        miss_color: tuple[int, int, int] = (40, 36, 50),
        curve_color: tuple[int, int, int] = (255, 200, 90),
        best_color: tuple[int, int, int] = (130, 255, 170),
        seed: int | None = None,
    ) -> None:
        self.dim = dim
        self.bg_color = np.array(bg_color, dtype=np.float32)
        self.match_color = np.array(match_color, dtype=np.float32)
        self.miss_color = np.array(miss_color, dtype=np.float32)

Try running it locally

The reference implementation is self-contained Python with numpy. Install the package and run:

python library_external/shiffman_pearson_textbook/geneticAlgorithmString/reference.py

Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.

More from shiffman · pearson

See all shiffman_pearson_textbook artworks →