How to Build Genetic Algorithms for Optimization Problems Using Microsoft's AI-For-Beginners Curriculum

You can build genetic algorithms for optimization problems by implementing a six-step evolutionary pipeline—encoding, fitness evaluation, variation operators, selection, replacement, and termination—using the self-contained Jupyter notebook at lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb in the Microsoft AI-For-Beginners repository.

The AI-For-Beginners repository provides a comprehensive, open-source curriculum for learning artificial intelligence concepts through interactive Jupyter notebooks. When you need to build genetic algorithms for optimization problems, the dedicated lesson demonstrates a complete, runnable implementation that handles both binary and permutation encodings. This educational resource breaks down the evolutionary process into discrete, understandable components that you can adapt to combinatorial challenges ranging from fair treasure splitting to the N-Queens problem.

Understanding the Genetic Algorithm Pipeline

The genetic algorithm implementation in the AI-For-Beginners curriculum follows a classic evolutionary optimization structure defined in lines 33-60 of Genetic.ipynb. The pipeline consists of six essential stages that transform a random population into an optimized solution through iterative refinement.

  1. Encoding: Solutions are represented as vectors—binary arrays for the fair treasure split problem or permutation arrays for the N-Queens problem.
  2. Fitness Evaluation: A scalar cost function measures solution quality, calculating absolute differences between partitions or counting attacking queen pairs.
  3. Variation Operators: Mutation flips random bits or swaps elements, while Crossover blends parent solutions by mixing genetic material.
  4. Selection: Parents are chosen probabilistically based on fitness weights, giving superior solutions higher reproduction chances.
  5. Replacement: The population updates by pruning weak candidates, introducing random individuals, and inserting offspring.
  6. Termination: The algorithm stops when fitness reaches zero or generation limits expire.

Implementing Solution Encoding and Fitness Evaluation

According to lines 78-84 of the notebook, the treasure-split problem uses binary encoding where each bit indicates subset membership. The fitness function calculates the absolute difference between partition sums to minimize disparity.

import random
import numpy as np

def generate(S):
    """Binary mask indicating which side of the split each element belongs to."""
    return np.array([random.randint(0, 1) for _ in S])

def fit(B, S):
    """Return the absolute difference between the sums of the two subsets."""
    c1 = (B * S).sum()
    c2 = ((1 - B) * S).sum()
    return abs(c1 - c2)

Creating Variation Operators

Lines 98-108 of Genetic.ipynb implement the mutation and crossover functions that introduce genetic diversity. The mutation operator performs random bit-flipping, while crossover creates offspring by randomly mixing parental genes.

def mutate(b):
    """Flip a single random bit."""
    x = b.copy()
    i = random.randint(0, len(b) - 1)
    x[i] = 1 - x[i]
    return x

def xover(b1, b2):
    """Randomly mix bits from two parents."""
    mask = generate(b1)          # 0/1 mask decides which parent contributes each gene

    return b1 * mask + b2 * (1 - mask)

Building the Evolution Loop

The evolve() function (lines 166-190) orchestrates the generational process, applying selection pressure through probabilistic operator choice. With a 30% mutation rate and 70% crossover rate, the function maintains population diversity while converging toward optimal solutions.

def evolve(pop, S, steps=2000):
    history = []
    for _ in range(steps):
        # Record best fitness in the current population

        best = min(fit(ind, S) for ind in pop)
        history.append(best)

        # Stop if we have an optimal solution

        if best == 0:
            break

        # 30% chance to mutate the worst individual

        if random.randint(1, 10) < 3:
            i = random.randint(0, len(pop) - 1)
            mutated = mutate(pop[i])
            worst = np.argmax([fit(ind, S) for ind in pop])
            pop[worst] = mutated
        else:
            # 70% chance to perform crossover

            i, j = random.sample(range(len(pop)), 2)
            child = xover(pop[i], pop[j])
            # Replace a parent if the child is better

            if fit(child, S) < fit(pop[i], S):
                pop[i] = child
            elif fit(child, S) < fit(pop[j], S):
                pop[j] = child
    # Return the best individual and the fitness history

    best_idx = np.argmin([fit(ind, S) for ind in pop])
    return pop[best_idx], history

Running the Complete Genetic Algorithm

The genetic() driver function (lines 260-300) initializes random problem instances and populations, then executes the evolutionary process. This entry point demonstrates how to build genetic algorithms for optimization problems with configurable population sizes and problem dimensions.

def genetic(N, pop_size=100):
    # Create a random problem instance

    S = np.array([random.randint(1, 10_000) for _ in range(N)])
    # Initialise population

    pop = [generate(S) for _ in range(pop_size)]
    # Run the evolutionary process

    solution, history = evolve(pop, S)
    print("Best partition difference:", fit(solution, S))
    return solution, history

# Example usage

solution, history = genetic(8)

Adapting to Different Problem Domains

While the treasure-split example uses binary encoding, the same repository demonstrates permutation encoding for the N-Queens problem. You can adapt this framework by modifying the generate() function to produce valid permutations and adjusting the fit() function to count constraint violations. The variation operators similarly require domain-specific adjustments—swap mutations for permutations rather than bit-flips—to maintain solution validity while exploring the search space.

Summary

  • The AI-For-Beginners repository at microsoft/AI-For-Beginners provides a complete genetic algorithm implementation in lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb.
  • The evolutionary pipeline requires six components: encoding, fitness evaluation, mutation, crossover, selection/replacement, and termination criteria.
  • Binary encoding works for subset selection problems like fair treasure splitting, while permutation encoding suits ordering problems like N-Queens.
  • The evolve() function implements steady-state replacement with probabilistic operator selection, balancing exploration and exploitation.
  • You can import these functions into any Python environment to solve custom combinatorial optimization problems.

Frequently Asked Questions

What types of optimization problems work best with genetic algorithms?

Genetic algorithms excel at combinatorial optimization problems where the solution space is large, discrete, and non-convex, such as scheduling, routing, and partitioning tasks. The AI-For-Beginners implementation specifically targets NP-hard problems like the fair treasure split and N-Queens, where traditional exhaustive search becomes computationally infeasible as problem size grows.

How does the fitness function influence genetic algorithm performance?

The fitness function serves as the sole guide for evolutionary pressure, directing the population toward optimal regions of the search space. In the repository's treasure-split example, minimizing the absolute difference between subset sums provides a clear gradient toward fair partitions; poorly designed fitness landscapes with deceptive local optima can cause premature convergence or stagnation.

Can I adapt this code for continuous optimization problems?

While the Microsoft notebook focuses on discrete binary and permutation encodings, you can adapt the framework for continuous optimization by modifying the generate() function to produce floating-point vectors and implementing Gaussian mutation or blend crossover operators. The core evolve() loop and selection mechanism remain applicable across both discrete and continuous domains.

Where can I find additional theory about genetic algorithm operators?

The lessons/6-Other/21-GeneticAlgorithms/README.md file provides high-level learning objectives and conceptual context, while lines 33-60 of Genetic.ipynb contain detailed markdown explanations of selection pressure, genetic drift, and operator probabilities. The repository also includes timing experiments comparing genetic algorithms to exhaustive search, illustrating computational complexity trade-offs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →