Genetic Algorithms › Formula

Michael BrenndoerferOctober 29, 202617 min read
Machine Learning from ScratchAll chapters
Part 15 of 15Heuristic and Evolutionary Optimization
Chapter 91Genetic Algorithms › Formula
Reading section 4 of 10 · Formula · 17 minSection 4 of 10 · 17 min

Section 4 of 10 · 17 min read

Genetic Algorithms › FormulaSection view · Sections in this chapter4 / 10 ↓
Reading tools Definitions, links & notes

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Notes & comments

The intuition behind a genetic algorithm is easy to state: keep several candidates alive, let good ones reproduce, and trust variation plus selection to improve the population. The formulas below make that intuition precise. They answer four questions in order: how a candidate is encoded and scored, how parents are chosen, how children are built without breaking the encoding, and how the next generation is chosen. Each piece exists for a reason, and the reasons are what make the algorithm more than a random search with extra steps.

Representation, fitness, and feasible solutions

We begin with the object being searched over. Because the decision is the order in which eight customers are visited, the natural genotype is a permutation. Let n=8n = 8, let C={1,…,8}\mathcal{C} = \{1,\dots,8\} index the customers, and let node 00 be the depot. A genotype is a permutation

p=(p1,p2,…,p8),{p1,…,p8}=C,p = (p_1, p_2, \dots, p_8), \qquad \{p_1,\dots,p_8\} = \mathcal{C},

where:

  • pp: the permutation (genotype), a length-8 ordering of the customers
  • pjp_j: the customer visited at position jj of the permutation
  • C\mathcal{C}: the set of customer indices {1,…,8}\{1,\dots,8\}
  • nn: the number of customers (n=8n=8), hence the length of every permutation

so the search space is X=S8X = S_8 with ∣X∣=8!=40320|X| = 8! = 40320 elements. This is already a strong constraint: every point in the search space is a valid ordering, and every ordering contains each customer exactly once. The algorithm never has to reason about missing or duplicated customers.

The permutation alone is not yet a pair of routes. The phenotype is produced by a decoder that cuts the permutation into two blocks of four and closes each block into a depot tour:

D(p)=([0,p1,p2,p3,p4,0],  [0,p5,p6,p7,p8,0]).D(p) = \big([0, p_1, p_2, p_3, p_4, 0],\; [0, p_5, p_6, p_7, p_8, 0]\big).

where:

  • D(p)D(p): the phenotype of permutation pp, a pair of closed depot tours
  • p1,…,p4p_1,\dots,p_4: the customers assigned to the first vehicle, visited in that order
  • p5,…,p8p_5,\dots,p_8: the customers assigned to the second vehicle, visited in that order
  • 00: the depot that opens and closes each tour

The decoder is doing two jobs at once. It assigns the first four customers to one vehicle and the last four to the other, and it makes every permutation feasible with respect to capacity, because each vehicle receives exactly four customers with qi=1q_i = 1 and Q=4Q = 4. That is not an accident of this instance: every customer demand is qi=1q_i = 1, every vehicle capacity is Q=4Q = 4, and the total demand ∑i∈Cqi=8\sum_{i \in \mathcal{C}} q_i = 8 equals the total capacity 2Q=82Q = 8. The decoder enforces the only capacity split that can work.

Writing d(a,b)d(a,b) for the Euclidean distance between nodes aa and bb, the objective is the total length of both tours: the sum of the two closed depot tours produced by the decoder, in which each route begins at the depot, visits its four assigned customers in permutation order, and returns to the depot. The sum has ten terms in total: two depot-to-first-customer arcs, six customer-to-customer arcs, and two last-customer-to-depot arcs.

f(p)  =  ∑r=12[ d ⁣(0, p4r−3)  +  ∑j=4r−34r−1d ⁣(pj, pj+1)  +  d ⁣(p4r, 0)].f(p) \;=\; \sum_{r=1}^{2}\Bigg[\, d\!\left(0,\,p_{4r-3}\right) \;+\; \sum_{j=4r-3}^{4r-1} d\!\left(p_j,\,p_{j+1}\right) \;+\; d\!\left(p_{4r},\,0\right) \Bigg].

where:

  • pp: the permutation (genotype) being scored, a length-8 ordering of the customers
  • pjp_j: the customer visited at position jj of the permutation
  • rr: the vehicle index, running over the two vehicles r=1r=1 and r=2r=2
  • 4r−34r-3: the first position of vehicle rr's block (11 for vehicle 1, 55 for vehicle 2)
  • 4r4r: the last position of vehicle rr's block (44 for vehicle 1, 88 for vehicle 2)
  • d(a,b)d(a,b): the Euclidean distance between nodes aa and bb
  • 00: the depot node
  • f(p)f(p): the summed length of both closed tours, the quantity minimised

Read the outer sum first: it runs over the two vehicles, r=1r=1 and r=2r=2. Inside each term there are three pieces:

  • the arc from the depot to the first customer of the route,
  • the three arcs that connect the four customers of the route in order,
  • the arc back to the depot.

The index arithmetic 4r−34r-3 is just bookkeeping that starts vehicle 1 at position 11 and vehicle 2 at position 55: for r=1r=1 we get 4r−3=14r-3=1 and the inner sum runs over j=1,2,3j=1,2,3; for r=2r=2 we get 4r−3=54r-3=5 and the inner sum runs over j=5,6,7j=5,6,7. The inner sum connects consecutive customers within a block, and the two outer distance terms close the block to the depot at both ends, so each route has five arcs: three customer-to-customer arcs connecting the four customers, plus the depot-to-first-customer arc and the last-customer-to-depot arc. Across both routes that is ten arcs, which matches the ten distance terms in the formula. The objective is a pure routing length: no vehicle cost, no lateness term, no service-time term. Every one of the ten arcs that the decoder creates is charged exactly once, and no arc is charged twice.

We use unit travel speed, so travel time equals Euclidean distance. Feasibility is checked by simulating each route. With sis_i the service start at node ii, aia_i the arrival, eie_i and ℓi\ell_i the window bounds, and τi\tau_i the service time, the recursion is

si=max⁡(ai, ei),anext=si+τi+d(i,next),\begin{aligned} s_i &= \max(a_i,\, e_i), \\ a_{\text{next}} &= s_i + \tau_i + d(i,\text{next}), \end{aligned}

where:

  • sis_i: the service start time at node ii, i.e. arrival held until the window opens
  • aia_i: the arrival time at node ii (the clock after traveling from the previous node)
  • eie_i: the opening of the time window at node ii
  • ℓi\ell_i: the closing of the time window at node ii
  • τi\tau_i: the service time at node ii
  • d(i,next)d(i,\text{next}): the Euclidean travel distance from node ii to the next node

The recursion is subject to si≤ℓis_i \le \ell_i for every node including the return to the depot, and to ∑i∈routeqi≤Q\sum_{i \in \text{route}} q_i \le Q, where qiq_i is the demand at node ii and QQ is the vehicle capacity. Here every customer demand is qi=1q_i = 1 and every vehicle capacity is Q=4Q = 4, and the total demand ∑i∈Cqi=8\sum_{i \in \mathcal{C}} q_i = 8 equals the total capacity 2Q=82Q = 8, so each vehicle must carry exactly four customers: the decoder enforces that automatically. All windows are [0,80][0, 80], and since every coordinate lies in [0,8]×[0,6][0,8]\times[0,6] no arc exceeds 82+62=10\sqrt{8^2+6^2}=10. A route has five arcs and serves four customers, each with service time τi=1\tau_i = 1. Starting from the depot, the service start at the fourth customer is at most 4⋅10+3⋅1=43<804\cdot 10 + 3\cdot 1 = 43 < 80, and the return to the depot takes one more arc of at most 1010 and one more service time of 11, giving 4⋅10+4⋅1+10=54<804\cdot 10 + 4\cdot 1 + 10 = 54 < 80. Every permutation is therefore feasible here. That is deliberate: the wide windows isolate search behaviour, and this instance does not test restrictive time-window handling.

Because we minimise, lower cost is better, and tournaments can compare costs without converting them into a separate fitness scale. For illustration, the positive transform F(p)=1/(1+f(p))F(p)=1/(1+f(p)) maps costs 38 and 40 to 1/391/39 and 1/411/41. The latter is 2/41≈4.882/41\approx4.88 percent smaller than the former. That transform compresses this particular cost difference; how concentrated proportional selection becomes depends on the full population. Our implementation instead selects the lowest raw cost in each sampled tournament.

On this symmetric-distance instance with deliberately wide windows, swapping the vehicles or reversing either tour preserves cost and feasibility. The decoded route sequences themselves change. We define a canonical signature σ(p)\sigma(p) by choosing the lexicographically smaller direction for each route and sorting the two resulting sequences. It identifies unordered pairs of undirected tours. Counting these signatures measures phenotype diversity; raw permutations and Hamming distance measure a different, genotype-level form of diversity that can also affect crossover.

Selection, crossover, mutation, and elitism

Selection is the pressure that turns variation into progress. Tournament selection draws kk distinct individuals uniformly at random from the population and returns the one with the lowest cost. It is simple, it needs no fitness scaling, and its selection pressure has a clean closed form.

For this rank calculation, assume distinct costs, or a fixed total tie order applied consistently when selecting a winner. In a population of size NN, rank r=1r=1 is best and rank r=Nr=N is worst. An individual of rank rr wins if it is drawn and no better individual (one of the r−1r-1 individuals with smaller rank) is drawn with it. The number of kk-subsets that contain it and otherwise contain only the N−rN-r worse individuals is (N−rk−1)\binom{N-r}{k-1}, out of (Nk)\binom{N}{k} equally likely subsets, so

P(select individual of rank r)  =  (N−r k−1 )(N k ).P(\text{select individual of rank } r) \;=\; \frac{\binom{N-r}{\,k-1\,}}{\binom{N}{\,k\,}}.

where:

  • NN: the population size
  • kk: the tournament size (the number of individuals drawn without replacement)
  • rr: the rank of the individual, with r=1r=1 the best and r=Nr=N the worst
  • (N−rk−1)\binom{N-r}{k-1}: the number of kk-subsets containing the individual and only worse individuals
  • (Nk)\binom{N}{k}: the total number of equally likely kk-subsets

Under that strict ranking, for N=20N=20 and k=3k=3 the unique best individual is selected with probability (192)/(203)=171/1140=0.15\binom{19}{2}/\binom{20}{3} = 171/1140 = 0.15, three times the uniform rate of 1/20=0.051/20 = 0.05, while the worst individual with rank r=20r=20, hence N−r=0N-r=0, has probability (02)/(203)=0\binom{0}{2}/\binom{20}{3} = 0. Raising kk sharpens the pressure; k=2k=2 is mild and k=Nk=N always picks the leader. Summing the expression over r=1,…,Nr = 1,\dots,N returns 11 by the hockey-stick identity, ∑r=1N(N−rk−1)=∑m=0N−1(mk−1)=(Nk)\sum_{r=1}^{N}\binom{N-r}{k-1} = \sum_{m=0}^{N-1}\binom{m}{k-1} = \binom{N}{k}, so it is a proper distribution. The formula describes selection under the stated ranking. Our code resolves a cost tie by the order in which contenders were sampled, so it does not assign a fixed strict rank to tied individuals. The rank formula alone does not predict a diversity-loss rate.

The selection pressure implied by this formula can be read directly. The following computation evaluates the rank probabilities for several tournament sizes.

PythonIn [2]
CollapseExpand
import math

import numpy as np

selection_n = 20
selection_ranks = np.arange(1, selection_n + 1)
selection_k_values = (2, 3, 5)
selection_probabilities = {
    k: np.array(
        [
            math.comb(selection_n - r, k - 1) / math.comb(selection_n, k)
            for r in selection_ranks
        ],
        dtype=float,
    )
    for k in selection_k_values
}
VisualizationOut [3]
CollapseExpand
Line chart of tournament selection probability by rank for three tournament sizes.
Tournament selection probability under a strict cost ranking for tournament sizes of 2, 3, and 5 in a population of 20. Larger tournaments concentrate probability on the best ranks and assign almost none to the worst ranks, which is the pressure that can remove diversity.

Order crossover (OX) builds the child from two cut positions. Fix cut positions 0≤a<b≤n−10 \le a < b \le n-1 and parents pp and qq, and index positions from 00 to n−1n-1 as in code:

  1. copy the block: ct=ptc_t = p_t for every t∈{a,…,b}t \in \{a,\dots,b\};
  2. let U={pa,…,pb}U = \{p_a,\dots,p_b\} be the values already used, and list the values of qq in cyclic order starting just after the block, qb+1,qb+2,…,qb+nq_{b+1}, q_{b+2}, \dots, q_{b+n} with indices taken modulo nn, discarding any value in UU;
  3. fill the remaining positions in the same cyclic order, b+1,b+2,…b+1, b+2, \dots modulo nn, with that filtered list.

where:

  • nn: the number of customers (n=8n=8), hence the length of each permutation
  • a,ba, b: the inclusive cut positions bounding the copied block of the first parent
  • pp: the first parent permutation, which supplies the copied block
  • qq: the second parent permutation, which supplies the remaining values in cyclic order
  • cc: the child permutation built by the rule
  • UU: the set of values already placed in the child from the copied block

The child is a permutation. The block contributes b−a+1b-a+1 distinct values; the filtered list contributes exactly the n−(b−a+1)n-(b-a+1) values of qq that are not in UU, in some order; the two position sets are disjoint and cover all nn positions. Nothing is duplicated and nothing is lost, so c∈Xc \in X with no repair step. OX preserves the copied block and the cyclic relative order of the remaining values from qq. Relative order does not preserve every adjacency: filtering out a value creates new neighbours, and the decoder may assign a value to a different vehicle. Interior adjacencies of the copied block survive, but even that block can cross the vehicle split. Feasible crossover is not an improvement guarantee.

Mutation in permutation space is a swap. Choosing two distinct positions and exchanging their values always yields a valid permutation, and there are (82)=28\binom{8}{2} = 28 possible swaps. Within a single route a swap touches the arcs incident to both positions, at most four arcs (three if the positions are adjacent). Swapping one position in the first block with one in the second block instead re-assigns two customers between vehicles, each touching its own incident arcs, so the change in objective can be much larger: several long arcs can change at once. Mutation is therefore a source of novelty, not a guarantee of improvement.

Elitism fixes the survival rule. With μ\mu parents and λ\lambda offspring per generation, (μ+λ)(\mu+\lambda) selection sets

Pt+1  =  top-⁡μ(Pt∪Ot),P_{t+1} \;=\; \operatorname*{top-}\mu\big(P_t \cup O_t\big),

Here PtP_t and OtO_t are indexed collections, and the union denotes concatenation with multiplicity: duplicate individuals still occupy separate slots. The μ\mu lowest-cost slots survive. Assume the objective is fixed and deterministic. In detail:

  • PtP_t: the population of parents at generation tt
  • OtO_t: the offspring produced at generation tt
  • μ\mu: the number of parents retained each generation (the population size)
  • top-⁡μ\operatorname*{top-}\mu: the operator that keeps the μ\mu lowest-cost individuals of the union. Because the old population is included among the candidates, the lowest retained cost is no larger than the old minimum, so the incumbent satisfies min⁡x∈Pt+1f(x)≤min⁡x∈Ptf(x)\min_{x\in P_{t+1}} f(x) \le \min_{x \in P_t} f(x): with elitism the best-so-far curve is non-increasing, and we record the incumbent after each evaluation in a separate archive. A running-minimum archive can provide such a trace under other survival rules too. The population may churn, but the best solution found so far never gets worse.

Finally, the budget identity. With a population of NN, the initial population costs 1+(N−1)=N1 + (N-1) = N evaluations (the shared nearest-neighbour score plus N−1N-1 random members), and each completed generation costs NN evaluations. The total is:

B  =  1⏟NN  +  (N−1)⏟random members  +  G⋅N.B \;=\; \underbrace{1}_{\text{NN}} \;+\; \underbrace{(N-1)}_{\text{random members}} \;+\; G \cdot N .

where:

  • BB: the total evaluation budget (here 300300)
  • 11: the single complete-route evaluation of the nearest-neighbour construction
  • N−1N-1: the random permutations that fill the rest of the initial population
  • NN: the population size, also the number of children charged per generation
  • GG: the number of generations

The displayed identity is for complete generations. In general B=N+GN+RB=N+GN+R with 0≤R<N0\le R<N, and the implementation can spend the remainder on a partial final generation. With B=300B=300 and N=20N=20, R=0R=0 and this gives G=⌊(300−N)/N⌋=⌊(300−20)/20⌋=⌊280/20⌋=14G = \lfloor (300 - N)/N \rfloor = \lfloor (300-20)/20 \rfloor = \lfloor 280/20 \rfloor = 14 generations. This identity is the accounting contract for the whole chapter: initialization, every child, and every duplicate are charged, so a comparison with the other methods is a comparison of equal evaluation budgets rather than equal wall-clock time.

The budget identity also makes the population-size trade-off explicit. The following computation tabulates the number of complete generations available for a range of population sizes at the same budget.

PythonIn [4]
CollapseExpand
import numpy as np

budget_for_generations = 300
population_sizes = np.arange(5, 51, 5)
generations = np.floor(
    (budget_for_generations - population_sizes) / population_sizes
).astype(int)
VisualizationOut [5]
CollapseExpand
Generations versus population size under a fixed 300-evaluation budget.
Completed generations as a function of population size for population sizes from 5 to 50 under the same 300-evaluation budget. Every additional population member costs one evaluation per full generation. Increasing population size leaves no more complete generations after initialization; the floor calculation produces plateaus as well as decreases.

Population diversity, repair, and premature convergence

Define the diversity of a generation as the number of distinct canonical signatures in the population, Dt=∣{σ(p):p∈Pt}∣D_t = |\{\sigma(p) : p \in P_t\}|, with Dt≤μD_t \le \mu, where:

  • DtD_t: the number of distinct canonical signatures in generation tt
  • σ(p)\sigma(p): the canonical signature of permutation pp (vehicle-symmetric, reversal-invariant)
  • PtP_t: the population of parents at generation tt
  • μ\mu: the number of parents (population size)

For populations with μ>1\mu>1, genotype-level diversity can also be measured with the mean pairwise Hamming distance 1μ(μ−1)∑i≠j∣{t:pt(i)≠pt(j)}∣\frac{1}{\mu(\mu-1)}\sum_{i \ne j} |\{t : p^{(i)}_t \ne p^{(j)}_t\}|, where:

  • p(i)p^{(i)}: the ii-th individual permutation in the population
  • tt: a position index, t=1,…,nt = 1,\dots,n
  • μ\mu: the number of parents (population size)

The sum runs over ordered pairs i≠ji \ne j, so each unordered pair is counted twice, matching the μ(μ−1)\mu(\mu-1) denominator. This measures genotype difference: swapping the two halves changes Hamming distance while preserving the unordered tour pair. The signature count instead measures distinct phenotypes under the stated symmetry; neither measure fully characterizes what crossover can produce.

Diversity is what makes crossover useful, and this can be shown rather than asserted. Order crossover applied to identical parents returns the parent: the block matches, and the filtered list from q=pq=p (the values of pp not copied into the block, in the cyclic order in which they appear in pp) is placed into the empty positions in cyclic order; the two cyclic orderings agree, so each value is placed back where it started. So if the population collapses to copies of one permutation, OX(p,p)=p\text{OX}(p,p) = p for every selected pair, and the only source of novelty is mutation. A population in that state is a single point with random kicks, which is precisely the trajectory we were trying to escape.

This loss of useful variation is one form of premature convergence. Selection can remove variants; mutation proposes new variants but does not guarantee their survival. A low mutation rate may leave identical parents reproducing copies. A high rate may perturb useful structures. The appropriate balance depends on the encoding, objective, survivor rule, and evaluation budget.

The balance is problem specific, and with a budget of only G=⌊(300−20)/20⌋=14G = \lfloor (300-20)/20 \rfloor = 14 generations there is little room to recover from a bad setting mid-run. We illustrate the trade-off with actual runs in the visualisation section rather than claiming a rule of thumb.

Repair is one alternative when feasibility is not enforced by construction. A repair operator RR maps an infeasible genotype back into the feasible set, and a penalty method folds violations into the objective:

fpen(p)=f(p)+λ⋅v(p)f_{\text{pen}}(p) = f(p) + \lambda \cdot v(p)

where:

  • RR: the repair operator, which takes any genotype and returns a feasible one
  • f(p)f(p): the routing length of permutation pp, as defined above
  • v(p)v(p): a nonnegative violation measure, larger when capacity or windows are exceeded
  • λ≥0\lambda \ge 0: the penalty weight that trades violation against routing length

The penalty objective compares violation and route cost on a chosen scale. A finite penalty need not make its minimizer feasible: a low route cost can outweigh the violation penalty. A validated repair may require computation or additional scoring, and many-to-one repair can reduce variation. Increasing λ\lambda amplifies violation differences rather than generally flattening the objective. Our decoder needs no repair on this wide-window instance. With restrictive windows, feasibility would need a suitable decoder, feasible moves, rejection, repair, penalties with explicit validation, or another constraint-handling rule.

Comments

Add to the conversation

A question, a correction, or an example from your own work.

No comments yet. Be the first to share your thoughts!

Newsletter

Stay up to date

Get new articles, book updates, and notes on AI and technology.

Unsubscribe at any time. Privacy policy