At their core, genetic algorithms are built on five foundational principles that closely resemble biological evolution:
1. Genes and Chromosomes
In biology, genes are units of information, and chromosomes are structured collections of those genes. In GAs, a chromosome is a single candidate solution, typically represented as an array, list, or string.
Each gene in the chromosome represents one aspect of the solution. For instance, in a string-matching problem, each character is a gene, and the entire string is the chromosome.
2. Population
A population is simply a collection of chromosomes. Rather than working on one solution, a GA evaluates a diverse set of solutions in parallel. This diversity is what enables GAs to avoid local optima and explore the solution space more thoroughly.
3. Fitness Function
The fitness function is the lens through which the algorithm views the world. It measures how “good” a solution is. It does not need to know the perfect answer, only how to rank the quality of candidate solutions.
In C#, this is typically implemented as a method returning an integer or floating-point score.
public int Fitness(string target) { return Genes.Zip(target, (g, t) => g == t ? 1 : 0).Sum(); }
This simple example scores a string based on how many characters match the target. The higher the score, the more “fit” the chromosome.
4. Selection
Once you’ve scored all chromosomes in the population, you need to decide which ones will pass their genes to the next generation. Selection mimics survival of the fittest: better solutions have a higher chance of being chosen to breed.
Common strategies include:
- Roulette Wheel Selection (probability weighted)
- Tournament Selection (random subset competitions)
- Elitism (carry over the best chromosomes unchanged)
We will implement these in detail later in the series.
5. Crossover and Mutation
Crossover is the process of combining genes from two parent chromosomes to create a new child. Mutation introduces randomness by slightly altering one or more genes in a chromosome.
Together, these operations drive both exploration (mutation) and exploitation (crossover) of the solution space.
Here is a quick crossover example:
public Chromosome Crossover(Chromosome partner) { int midpoint = Genes.Length / 2; string childGenes = Genes.Substring(0, midpoint) + partner.Genes.Substring(midpoint); return new Chromosome(childGenes); }
And a simple mutation implementation:
public Chromosome Mutate(double mutationRate) { var mutated = Genes.Select(c => _random.NextDouble() < mutationRate ? GenePool[_random.Next(GenePool.Length)] : c); return new Chromosome(new string(mutated.ToArray())); }
Mutation rates are typically small, ranging from 0.1% to 5%, depending on the domain.
The GA Lifecycle
Every genetic algorithm runs through a loop that looks like this:
- Initialize a random population of chromosomes
- Evaluate each chromosome using the fitness function
- Select the best chromosomes to become parents
- Crossover parents to produce children
- Mutate the children randomly
- Replace the old population with the new one
- Repeat until a stopping condition is met (e.g., max generations or fitness threshold)
This loop is both simple and powerful. Over time, poor solutions are discarded, decent solutions evolve into better ones, and the population converges on high-quality answers—even when the shape of the solution is not known in advance.
Why It Works
GAs are not guaranteed to find the perfect solution, but they often find good solutions when traditional methods fail. Their power comes from combining local search (mutation) with global recombination (crossover), guided by a measurable goal (fitness function).
In domains where the search space is discontinuous, high-dimensional, or has many local optima, GAs offer a viable and surprisingly effective alternative to brute-force or greedy algorithms.
Up Next
In the next post, we will dive into modeling chromosomes and genes in C#. We’ll create a flexible Chromosome
class that supports fitness evaluation, crossover, and mutation. By the end of the week, you’ll have a functioning genetic algorithm ready to solve a basic optimization problem.
Evolution is coming to your codebase. Get ready.