Why Genetic Algorithms Matters
Suppose a candidate solution is a six-bit configuration. The left three bits and right three bits control two interacting groups of settings. One candidate gets the left group right, another gets the right group right, and changing only one bit may make either candidate worse. A search that maintains several candidates can recombine useful parts, then use occasional mutations to explore configurations that the current population does not contain.
A genetic algorithm, or GA, is a family of population-based optimization heuristics. It repeatedly evaluates candidates, favors fitter candidates as parents, combines their representations, introduces variation, and forms a new population. Here higher fitness is better. The result is the best evaluated candidate, together with a reproducible record of the search.
The lesson builds a specific binary, generational GA with two-way tournament selection, one-point crossover, bit mutation, one copied elite, and an independent best-ever archive. You will trace every child and run matching Python, C++, and Java programs. A finite run can find a good solution without establishing that no better solution exists; iteration limits and apparent convergence are stopping rules, not optimality proofs.
Use a genetic algorithm to explore an optimization space by evolving a population: select parents using their fitness, recombine parts of their encodings, and mutate offspring. Preserve the best evaluated solution, and treat the final answer as a heuristic result unless an independent bound or exact method certifies optimality.
Core Concepts and Terminology
A genotype is the stored encoding; a phenotype is the solution it represents. In this lesson they coincide: an unsigned $L$-bit string denotes an integer $x$ with $0\le x\lt2^L$. A deterministic table supplies its fitness $f(x)$. A population $P_g$ is an ordered list of $N$ genomes at generation $g$; duplicates are allowed and occupy separate slots.
We maximize fitness, so parent selection compares values directly. Negative scores are valid: $-1$ is fitter than $-3$. Tournament selection needs no division by fitness, no positivity assumption, and no normalization. For a minimization objective $E$, one option is $f=-E$ when its numeric range remains valid.
The worked objective is deliberately deceptive. For a three-bit block containing $u$ ones, define
$$t(u)=\begin{cases}3,&u=3,\\2-u,&0\le u\lt3.\end{cases}$$
For six bits, sum the two block scores:
$$f(x)=t(u_{\mathrm{left}})+t(u_{\mathrm{right}}).$$
For example, 000000 scores $2+2=4$, 111000 scores $3+2=5$, and 111111 scores $3+3=6$. A single-bit change from 000000 reduces the score to $3$, so it is a strict local maximum for single-bit hill climbing. The unique global optimum is 111111: each block contributes at most $3$, with equality only at 111.
The landscape diagram groups each block by its number of ones; its horizontal distance is not the integer distance between whole genomes. GA moves have no fixed step size: crossover can change several loci, mutation can change zero through $L$ bits, and each generation contains several trajectories. An unchanged best score can indicate a plateau or a stalled search without certifying local or global optimality.
| Term | Concrete example | Role |
|---|---|---|
| Genome / chromosome | 111000, stored as integer $56$ | Encodes one candidate |
| Gene / locus / allele | Leftmost bit; allele 1 | Position and its stored value |
| Fitness | $f(\texttt{111000})=5$ | Larger values win comparisons |
| Population | [000000,000111,111000,010101] | Ordered candidates, with repetition allowed |
| Tournament | Sample indices $(2,1)$ and choose the fitter | Selects a parent using two samples with replacement |
| Crossover point | 111 prefix plus 111 suffix, cut $c=3$ | Combines two parents into one child |
| Mutation rate | $p=m/1000$, with $m=100$ | Each bit flips on a mapped draw below $100$ |
| Elite | Earliest current slot with maximum fitness | Copied unchanged into next slot $0$ |
| Archive | First evaluated genome attaining the best score seen | Returned even after later exploration |
| Generation | One elite plus $N-1$ new evaluated children | Replaces the entire parent list at once |
| Evaluation budget | Initialization costs $N$; each generation costs $N-1$ | Bounds objective calls rather than generation count alone |
How Genetic Algorithms Works, Step by Step
The inputs specify the bit length, deterministic fitness table, initial ordered population, seed, generation limit $G$, mutation threshold $m$, and evaluation budget $B$. Initialization is supplied explicitly so that comparisons do not hide the cost or randomness of creating the initial population.
- Validate, then initialize. Copy the inputs. If fewer than $N$ evaluations remain, return
EVAL_LIMITwith no evaluated solution and no random draws. Otherwise evaluate all initial slots, including duplicates. Select the earliest maximum as the initial archive, and record generation $0$. - Check stopping rules. The generation limit is checked first. If it has been met, return
GEN_LIMIT. Otherwise require room for all $N-1$ new evaluations. If that room is absent, returnEVAL_LIMITbefore drawing randomness or creating a partial generation. - Copy one elite. Find the earliest maximum in the current population and put a value copy into next slot $0$, retaining its cached fitness. The objective is deterministic, so this unchanged elite needs no new evaluation.
- Select two parents for each remaining slot. Draw four old-population indices, with replacement. The first pair forms tournament A and the second pair tournament B. Higher fitness wins; a tie keeps the first sampled index, not the smaller index. Parents may be identical. All tournaments read the frozen old population, including when earlier children are already available.
- Crossover once. For $L\gt1$, choose a cut $c$ uniformly from $1,\ldots,L-1$ in the ideal random model. Keep the first $c$ displayed bits of parent A and the final $L-c$ bits of parent B. Produce one child per slot. For $L=1$, use $c=0$ without drawing a cut, so the unmutated child is parent B.
- Mutate each locus. Visit bits from left to right. Draw one integer $v$ from $0,\ldots,999$ for every bit, including at rates $0$ and $1000$. Flip that bit precisely when $v\lt m$. With independent uniform draws this is independent bit mutation with probability $p=m/1000$ per locus.
- Evaluate and archive. Evaluate the mutated child once, append its genome and score, and update the archive only for a strictly larger fitness. Log the tournaments, cut, pre-mutation child, mutation draws, mask, final child, score, and generator state.
- Commit the next population. After all child slots are filled, replace the old population and its scores together, increment the generation, record a snapshot, and repeat.
For integer bit operations, a suffix mask $s=2^{L-c}-1$ selects the low bits. With the full mask $M=2^L-1$, crossover is
$$x_{\mathrm{cross}}=(a\mathbin{\&}(M\mathbin{\oplus}s))\mathbin{\vert}(b\mathbin{\&}s).$$
Mutation constructs a flip mask $h$ and computes $x_{\mathrm{child}}=x_{\mathrm{cross}}\mathbin{\oplus}h$. Here $\&$, $\vert$, and $\oplus$ mean bitwise AND, OR, and XOR. A bit may flip even when it was inherited correctly; the archive and elite protect discoveries from those later changes.
flowchart TD
A["Validate and copy inputs"] --> B{"Budget covers initialization?"}
B -->|No| Z["Return no evaluated solution"]
B -->|Yes| C["Evaluate initial population and archive best"]
C --> D{"Generation limit reached?"}
D -->|Yes| Y["Return archive and completed population"]
D -->|No| E{"Budget covers a full generation?"}
E -->|No| Y
E -->|Yes| F["Copy one elite into next population"]
F --> H["Select two parents from frozen population"]
H --> I["Crossover then mutate every bit"]
I --> J["Evaluate child and update archive"]
J --> K{"More child slots?"}
K -->|Yes| H
K -->|No| M["Commit next population and record generation"]
M --> D
Worked Example with a Full Trace
Use $L=6$, $N=4$, seed $113$, $G=3$, $m=100$, and $B=100$. The initial population is [000000,000111,111000,010101], with scores $[4,5,5,1]$. Its total fitness is $15$ and its mean is $15/4=3.75$. The first tied maximum is 000111, so that genome enters the archive.
Randomness comes from the integer recurrence
$$z_{k+1}=48271z_k\bmod2147483647,\qquad z_0=113.$$
Each raw draw is a state $z$ from $1,\ldots,2147483646$. To obtain an integer below a positive bound $b$, let $R=2147483646$, set $v=z-1$, and accept it only if
$$v\lt R-(R\bmod b).$$
Return $v\bmod b$ after acceptance; otherwise draw again. This rejection mapping gives equal-sized residue classes under a uniform raw-input model, unlike unrestricted modulo mapping when $b$ does not divide $R$. The generator itself is a deterministic teaching generator; its outputs are correlated and are not independent mathematical randomness.
For every child, the order of mapped draws is four tournament indices, one cut draw if $L\gt1$, then $L$ mutation draws. The stored cut adds $1$ to its mapped value. No rejection occurs in this sample, so each child consumes $4+1+6=11$ raw draws and each full generation consumes $33$. For example, the first raw state is $5454623$, giving first tournament index $(5454623-1)\bmod4=2$.
The following table records all nine children. Indices refer to the preceding generation, and A/B lists the tournament winners. The six mutation draws are in displayed left-to-right bit order. A 1 in the mask marks a flipped bit.
| Generation / slot | Tournament samples; winners A/B | Cut | Before mutation | Six mutation draws | Flip mask | Child / fitness |
|---|---|---|---|---|---|---|
| $1/1$ | $(2,2)$, $(0,0)$; $2/0$ | $5$ | 111000 | 591,666,596,319,554,452 | 000000 | 111000 / $5$ |
| $1/2$ | $(1,3)$, $(3,2)$; $1/2$ | $3$ | 000000 | 443,257,944,211,684,848 | 000000 | 000000 / $4$ |
| $1/3$ | $(0,3)$, $(1,3)$; $0/1$ | $4$ | 000011 | 27,880,859,933,313,932 | 100000 | 100011 / $1$ |
| $2/1$ | $(3,3)$, $(3,0)$; $3/0$ | $1$ | 100111 | 348,194,609,549,861,375 | 000000 | 100111 / $4$ |
| $2/2$ | $(2,1)$, $(2,0)$; $1/0$ | $3$ | 111111 | 243,697,367,241,531,370 | 000000 | 111111 / $6$ |
| $2/3$ | $(1,0)$, $(0,0)$; $1/0$ | $2$ | 110111 | 766,210,548,427,366,356 | 000000 | 110111 / $3$ |
| $3/1$ | $(3,1)$, $(1,3)$; $1/1$ | $1$ | 100111 | 607,854,98,30,742,584 | 001100 | 101011 / $0$ |
| $3/2$ | $(3,3)$, $(3,0)$; $3/0$ | $1$ | 100111 | 946,883,413,615,845,776 | 000000 | 100111 / $4$ |
| $3/3$ | $(2,1)$, $(0,0)$; $2/0$ | $4$ | 111111 | 783,463,142,178,103,392 | 000000 | 111111 / $6$ |
The child in generation $2$, slot $2$, combines 111000 and 000111 at cut $3$, obtaining 111111 without mutation. The next child in that same generation still selects from generation $1$; it cannot select this newly produced optimum. In generation $3$, slot $1$, draws $98$ and $30$ flip two bits of 100111, creating the poor genome 101011 with score $0$.
These are the completed populations and cumulative counters:
| Generation | Ordered population | Fitness values | Total / mean | Archive | Evaluations / raw draws |
|---|---|---|---|---|---|
| $0$ | 000000,000111,111000,010101 | 4,5,5,1 | $15$ / $3.75$ | 000111 / $5$ | $4/0$ |
| $1$ | 000111,111000,000000,100011 | 5,5,4,1 | $15$ / $3.75$ | 000111 / $5$ | $7/33$ |
| $2$ | 000111,100111,111111,110111 | 5,4,6,3 | $18$ / $4.5$ | 111111 / $6$ | $10/66$ |
| $3$ | 111111,101011,100111,111111 | 6,0,4,6 | $16$ / $4$ | 111111 / $6$ | $13/99$ |
The final status is GEN_LIMIT, with archive 111111:6, $13$ evaluations, $9$ children, $3$ flipped bits, $99$ raw draws, no rejection retries, and final random state $501268393$. The mean drops from $4.5$ to $4$ in the final generation while best fitness remains $6$.
For complete generator accounting, the child-end states and cumulative draw counts are:
| Generation / slot | End random state | Cumulative raw draws |
|---|---|---|
| $1/1$ | $255331453$ | $11$ |
| $1/2$ | $308686849$ | $22$ |
| $1/3$ | $1837409933$ | $33$ |
| $2/1$ | $1023921376$ | $44$ |
| $2/2$ | $1164408371$ | $55$ |
| $2/3$ | $1315670357$ | $66$ |
| $3/1$ | $204869585$ | $77$ |
| $3/2$ | $882111777$ | $88$ |
| $3/3$ | $501268393$ | $99$ |
The run has found the global optimum because the independent block bound proves $f(x)\le6$ and its archive reaches $6$. GEN_LIMIT itself carries no such guarantee. The implementation deliberately keeps running after finding this value because it accepts arbitrary tables and has no built-in optimum target.
Trace It by Hand
Before generation $2$, the frozen population is [000111:5,111000:5,000000:4,100011:1]. For child slot $2$, the tournament samples are $(2,1)$ and $(2,0)$, the cut is $3$, and mutation draws are $(243,697,367,241,531,370)$ with threshold $100$. Predict both parents, the child, and the new archive.
Reveal: the parents are old indices $1$ and $0$, namely 111000 and 000111. Their prefix/suffix combination is 111111; every mutation draw is at least $100$, so no bit flips. Its fitness $6$ strictly improves the archive from $5$ to $6$. The next child's tournaments still use the same frozen parent population.
Implementation in Python, C++, and Java
Each panel is a complete program with the same supplied population and integer random source. Run Python $3.10+$, compile C++ as C++17, or save the Java panel as Main.java and use Java $11+$.
genetic_algorithm accepts $1\le L\le12$, a table of exactly $2^L$ integer scores in $[-10^9,10^9]$, and $2\le N\le32$ valid genome integers. The seed must be in $1,\ldots,2147483646$; the requested generations are $0$ through $500$; the mutation threshold is $0$ through $1000$; and the evaluation budget is $0$ through $10^6$. Python also rejects booleans and non-integer numeric parameters or elements. The inputs must be ordinary finite sequences; Java arrays must be non-null.
The result holds status, has_value, archive, final population and matching scores, completed generations, evaluation/child/bit-flip counts, generator state/draw/rejection counts, generation snapshots, and child events. If initialization cannot fit, has_value is false, the final population and histories are empty, best index is $-1$, and the zero fitness field is a dummy value. Invalid inputs raise an exception before searching.
An evaluation means one lookup in this teaching table. Duplicate genomes are evaluated again in child slots; only the unchanged elite's score is reused. A budget of $6$ initializes four members but leaves room for only two of the three required children, so the program stops at generation $0$ without drawing randomness. A zero-generation request still evaluates the initial population if its budget permits; after initialization, GEN_LIMIT takes precedence when both limits would stop the run.
All state arithmetic uses integers. C++ and Java use signed 64-bit values for the generator product, score values, and population totals, which can reach magnitude $32\times10^9$. Genomes and shifts remain safely bounded by the validated length. Crossover and mutation produce new scalar genomes, and population snapshots are copied so later generations cannot alter earlier history.
The sample includes the full trap run, an insufficient-generation budget, a zero budget, and a one-bit population with negative scores and mutation rate $1$. The last case shows that the elite remains unchanged even when every bit of every newly generated child flips.
Teaching adapter versus a real objective: this program receives the complete fitness table. If that table is already known, a linear scan over its $2^L$ entries finds the exact optimum directly, and there is no reason to use a GA to solve that small problem. The table makes objective behavior transparent and independently checkable. A practical GA normally calls an expensive evaluator without precomputing the search space; account for those actual evaluations and initialization costs when adapting it.
Expected standard output, identical in all three languages:
trapg=0 pop=000000,000111,111000,010101 fitness=4,5,5,1 best=000111:5 sum=15 eval=4 draws=0g=1 pop=000111,111000,000000,100011 fitness=5,5,4,1 best=000111:5 sum=15 eval=7 draws=33g=2 pop=000111,100111,111111,110111 fitness=5,4,6,3 best=111111:6 sum=18 eval=10 draws=66g=3 pop=111111,101011,100111,111111 fitness=6,0,4,6 best=111111:6 sum=16 eval=13 draws=99status=GEN_LIMIT best=111111:6 generations=3 eval=13 children=9 flips=3 draws=99 rejections=0 rng=501268393budgetg=0 pop=000000,000111,111000,010101 fitness=4,5,5,1 best=000111:5 sum=15 eval=4 draws=0status=EVAL_LIMIT best=000111:5 generations=0 eval=4 children=0 flips=0 draws=0 rejections=0 rng=113zero-budgetstatus=EVAL_LIMIT best=NONE generations=0 eval=0 children=0 flips=0 draws=0 rejections=0 rng=113one-bitg=0 pop=0,0 fitness=-3,-3 best=0:-3 sum=-6 eval=2 draws=0g=1 pop=0,1 fitness=-3,-1 best=1:-1 sum=-4 eval=3 draws=5g=2 pop=1,0 fitness=-1,-3 best=1:-1 sum=-4 eval=4 draws=10status=GEN_LIMIT best=1:-1 generations=2 eval=4 children=2 flips=2 draws=10 rejections=0 rng=439347582
Complexity, Correctness, and Invariants
Let $g$ be the number of completed generations and $C=g(N-1)$ the number of evaluated children. For an initialized run, the exact evaluation count is
$$E=N+g(N-1),\qquad g=\min\left(G,\left\lfloor\frac{B-N}{N-1}\right\rfloor\right).$$
When $B\lt N$, no evaluation occurs. Fitness values do not affect how many generations this implementation completes: it has no early optimum or stagnation exit. With sufficient budget, best, average, and worst objective landscapes all require exactly $N+G(N-1)$ evaluations. Random draw counts can still differ because the integer mapper sometimes rejects a raw value.
Write $D$ for the number of raw generator draws and $J$ for rejection retries. The exact relation is
$$D=C\bigl(4+\mathbf{1}_{L\gt1}+L\bigr)+J.$$
For the actual table-based implementation, including input validation/copies and retained traces, running time is
$$\Theta\bigl(2^L+N+gN+CL+D\bigr).$$
For the particular generator and allowed bounds, each mapped draw takes at most two raw draws. A rejected state is $z=2147483647-k$ for some $1\le k\le999$; its successor is $2147483647-48271k$, which lies below every rejection interval used here. Thus $J$ is at most the number of requested mapped draws, and this bounded implementation has best-, average-, and worst-case time $\Theta(2^L+N+gNL)$ for fixed input sizes and completed generation count. Displaying all genomes adds $O((g+1)NL)$ output work.
If the raw generator were replaced by an independent uniform random source, rejection sampling would instead have expected constant cost, with no fixed worst-case retry bound. The explicit $D$ expression remains valid in either model; the two-draw bound relies on this generator, multiplier, and small validated bounds.
With an external fitness evaluator costing $C_f$ per child and ordinary $L$-gene variation, the usual expected search cost is $O(NC_f+gN(L+C_f))$, plus representation construction and validation. There is no general polynomial bound on generations needed to discover an optimum. A GA's quality and runtime depend on the encoding, operators, objective, and stopping policy.
Two live populations need $O(NL)$ space for explicit gene arrays, or $O(N)$ machine words for this packed bounded encoding. This program keeps full history: its auxiliary storage is $\Theta(2^L+(g+1)N+C(L+1))$ words, including the copied fitness table, generation snapshots, and every child's mutation draws. Removing history and using an external evaluator leaves the population storage plus the archive and evaluator state.
The proof of the useful guarantees is an induction over generations. Initialization pairs each genome with its exact deterministic score and sets the archive to their maximum. During a generation, all parents and comparisons refer to that unchanged pair of old arrays. Crossover masks and mutation masks contain only the lowest $L$ bits, so every child is a valid genome; evaluating it restores the exact genome/fitness pairing. Strict archive updates preserve the maximum over every evaluated initial member and child. Copying the current elite retains the previous maximum, so after committing a complete generation,
$$\max_{x\in P_{g+1}}f(x)\ge\max_{x\in P_g}f(x).$$
In this implementation that maximum equals the archive's score at every recorded generation. The mean and minimum fitness need not improve. Reserving the whole generation's evaluations before creating children ensures the returned population and history always end at a completed generation.
One theoretical distinction matters. Under fresh independent bit mutation with $0\lt p\lt1$, any pre-mutation genome reaches any particular target at Hamming distance $h$ with probability $p^h(1-p)^{L-h}$. This is at least $\varepsilon=\min(p,1-p)^L\gt0$. Therefore after $C$ offspring the probability of never sampling a fixed optimum is at most $(1-\varepsilon)^C$, even when the parental genomes adapt to previous outcomes. An unlimited ideal run samples that optimum with probability $1$, but this loose bound may be astronomically unhelpful. It does not establish a finite-run certificate, does not apply at $p=0$ or $p=1$, and does not turn this finite deterministic PRNG into an independent random source.
| Method | Work for a stated search budget | Live search storage, excluding traces | Guarantee and assumptions |
|---|---|---|---|
| This binary GA | Expected $O(NC_f+gN(L+C_f))$ search work | $O(NL)$ for explicit chromosomes | Best evaluated candidate; deterministic objective and correct elitism; copied table adds $O(2^L)$ here |
| Exhaustive enumeration | $\Theta(2^L C_f)$ evaluations/work for a full binary domain | $O(L)$ plus best witness | Global optimum after every feasible genome is checked |
| Single-bit hill climbing | $O(KL C_f)$ for $K$ full neighbor scans | $O(L)$ | Can certify a single-bit local maximum after a full unsuccessful scan |
| Simulated annealing | $O(K(L+C_f))$ for $K$ proposals with full candidate copying | $O(L)$ plus best witness | Explores through occasional worse moves; finite geometric schedules give no general global certificate |
| Random search | $O(K(L+C_f))$ for $K$ independently sampled candidates | $O(L)$ plus best witness | Simple evaluation-budget baseline; quality depends on sampling distribution |
| Exact branch and bound | Worst case exponential in $L$ | Depends on search strategy | Can certify optimality when bounds are valid and all relevant branches are resolved |
Every stored score matches its genome, every recorded population has exactly $N$ members, and the archive is the best fitness among all evaluated genomes. Copying one unchanged elite makes recorded population-best fitness nondecreasing; these invariants guarantee consistent search results and preservation, not global optimality.
Variants, Pitfalls, and Optimizations
Choose operators for the representation. Bit-flip mutation and one-point crossover preserve binary strings. They do not preserve permutations, precedence constraints, route feasibility, or fixed-cardinality subsets. For a tour, use permutation-aware crossover and swap or inversion mutation; for constraints, define a feasible decoder, repair rule, or explicit constraint-handling policy. A repaired offspring must be evaluated in its final form.
Keep selection and fitness direction consistent. This code maximizes. A tournament handles zero and negative values correctly; ordinary roulette-wheel probabilities proportional to raw fitness fail when weights are negative or their total is zero. For a single objective, a strictly increasing fitness transformation preserves tournament winners, although fitness totals and stopping thresholds may change. Penalized objectives require deliberate tradeoffs, not arbitrary constants.
Separate the archive from breeding elitism. An archive can remember a lost candidate without keeping it available for reproduction. Elitism actively inserts that candidate into the next population. Excessive elitism and strong tournaments may remove diversity; too little selection pressure can spend evaluations almost randomly. Equal best scores or many duplicate genomes do not prove that the whole feasible space has been explored.
Preserve parent and score integrity. With mutable chromosomes, selecting an individual twice may create aliases. Clone before changing its genes, preserve the old generation until replacement, and invalidate any cached score after variation. The integer encoding here avoids chromosome aliasing, but snapshots still require independent list/array copies. DEAP's operator documentation documents the same ownership issue for its in-place variation operators.
Interpret mutation probabilities precisely. This implementation mutates every child and makes a separate decision per bit. Under the ideal model, the expected number of flips is $Lp$, and the probability of at least one flip is $1-(1-p)^L$. For $L=6$ and $p=0.1$, those are $0.6$ flips and probability $0.468559$; it is incorrect to call this a $10\%$ probability of mutating the whole child. A rate near $1/L$ is a useful experiment on some binary benchmarks, not a universal best setting. Mutation rate $1$ complements every child; it does not sample uniformly. Rate $0.5$ produces a uniform random bit string conditional on any pre-mutation child under independent bit draws, largely erasing parental information.
Do not equate crossover with improvement. In the trace, joining 111000 with 000111 at cut $3$ produces 111111, while reversing those parent roles at the same cut produces 000000. Useful recombination depends on which loci interact and how the encoding groups them; a cut can break a good block. Uniform, two-point, and problem-specific crossovers explore different combinations.
Measure quality against evaluations. Compare independent seeds, initialization schemes, and exact small-instance optima; report success rate and best-score distributions at the same evaluation budget. Include random search and simpler local methods as baselines. A fixed seed supports reproducibility, while many seeds assess variability. Parallel offspring evaluation can help when objectives are expensive; preserve deterministic slot order, per-child random streams, and tie rules if you need reproducible results.
Cache and stop carefully. Caching deterministic duplicate genomes can reduce costly objective calls, but changes what an evaluation-budget counter means; the shown code intentionally evaluates each child. For noisy or time-changing fitness, elite score reuse and monotonic best-value claims require reevaluation or an explicit statistical policy. A stagnation exit saves work but is still heuristic. Restarts, random immigrants, islands, and fitness sharing can encourage diversity; memetic algorithms combine evolutionary variation with local improvement. Real-valued evolutionary strategies and multiobjective methods such as NSGA-II solve different representation or selection problems; their settings and guarantees must be assessed separately.
Keep parents, modified offspring, cached fitness, and history consistent: aliasing or stale scores can make selection and the reported best meaningless. Preserve an elite if monotone population-best fitness is required, and assess search quality with evaluation budgets and repeated seeds rather than declaring global convergence from a plateau.
Practice Problems and Real-World Use
Start with a target-bit or OneMax objective whose optimum is known, then move to a small deceptive landscape like this lesson's. Replace the table with an evaluator only after the small cases have exact reference answers. DEAP's OneMax example is a primary worked example of the selection/variation/evaluation loop; its operator reference is useful for checking representation-specific choices. Those examples use their own GA policies, so do not expect their traces to match the particular policy defined here.
For a practical optimization study, record the encoding and decoder, objective and constraints, initial population, seed policy, operators and their probabilities, tie rules, budget accounting, and independent validation of the final candidate. In engineering design or scheduling, evaluator cost and feasibility often matter more than the arithmetic of the genetic operators. A benchmark score is meaningful only when compared with an appropriate exact bound or baseline.
| Problem or application | Exercise | Main idea tested | Difficulty |
|---|---|---|---|
| OneMax and target matching | Maximize matched bits; compare with the known optimum | Fitness direction, bit mutation, evaluation accounting | Easy |
| Two deceptive three-bit blocks | Reproduce the full trace and try several seeds | Recombination, local traps, archive versus mean fitness | Medium |
| Exact small-table laboratory | Enumerate every genome and compare equal-budget methods | Optimality gaps and reproducible baseline comparisons | Medium |
| Binary feature selection | Decode selected features and use separate validation data | Constraint handling, expensive evaluation, overfitting control | Hard |
| Knapsack design | Encode selected items and enforce capacity explicitly | Feasibility repair, penalties, and exact dynamic-programming checks | Medium |
| Traveling-salesperson tours | Use permutation-aware operators and verify every city occurs once | Representation invariants and route-cost evaluation | Hard |
| Scheduling or engineering design | Define a legal decoder and compare multiple seeded runs | Domain constraints, evaluator cost, and reproducible experiments | Hard |
| Diversity and island experiment | Compare immigrants, restarts, or isolated subpopulations | Premature convergence and quality per evaluation budget | Advanced |