liveBook · Manning

Fitness

As explained in Classic Computer Science Problems in Swift: Essential techniques for practicing programmers

A function that assigns a score indicating how well a candidate solution satisfies the problem objective.

In a genetic algorithm, fitness is a score for how well a chromosome solves a problem. Higher scores make a chromosome more likely to be selected as a parent. The algorithm can stop when a score meets a threshold; otherwise, it returns the best candidate found within its generation limit. Because selection, crossover, and mutation involve chance, that candidate is not guaranteed to be optimal. section 5.1

A Swift `Chromosome` exposes its score as `var fitness: Double { get }`. The generic algorithm measures fitness each generation, caches the scores for selection, and tracks the best candidate. A useful fitness calculation must therefore rank candidates in the direction the algorithm expects: higher is better. section 5.3 section 5.5

Maximize an equation

Let a chromosome's genes be integers `x` and `y`. Its fitness can return `Double(6*x - x*x + 4*y - y*y)`. The highest value is 13, at `x = 3` and `y = 2`, so 13 is a suitable stopping threshold when that answer is known. Without a known target, use a high threshold and inspect the best result after a fixed number of generations. section 5.4

Score distance from a solution

For SEND+MORE=MONEY, calculate `difference = abs(MONEY - (SEND + MORE))` and return `1 / Double(difference + 1)`. A difference of zero scores 1; larger errors score less. Return 0 if a required letter is missing. This turns a distance to minimize into a score to maximize, though the conversion can introduce bias. section 5.5

The same choice arises when adapting constraint satisfaction to a genetic algorithm: one possible fitness score is the number of constraints a chromosome satisfies. section 5.8

public protocol Chromosome {
    var fitness: Double { get } // how well does this individual solve
     the problem?
    init(from: Self) // must be able to copy itself
    static func randomInstance() -> Self
    func crossover(other: Self)
     -> (child1: Self, child2: Self) // combine with other to form children
    func mutate() // make a small change somewhere
    func prettyPrint()
}

The `Chromosome` interface places fitness alongside the operations that create and change candidates. section 5.3

final class SendMoreMoney: Chromosome {
    var genes: [Character]
    static let letters: [Character] = ["S", "E", "N", "D", "M", "O", "R",
     "Y", " ", " "]

    var fitness: Double {
        if let s = genes.index(of: "S"), let e = genes.index(of: "E"), let
          n = genes.index(of: "N"), let d = genes.index(of: "D"),
          let m =  genes.index(of: "M"), let o = genes.index(of: "O"),
          let r =  genes.index(of: "R"), let y = genes.index(of: "Y") {
            let send: Int = s * 1000 + e * 100 + n * 10 + d
            let more: Int = m * 1000 + o * 100 + r * 10 + e
            let money: Int = m * 10000 + o * 1000 + n * 100 + e * 10 + y
            let difference = abs(money - (send + more))
            return 1 / Double(difference + 1)
        }
        return 0
    }

    init(from: SendMoreMoney) {
        genes = from.genes
    }

    init(genes: [Character]) {
        self.genes = genes
    }

    static func randomInstance() -> SendMoreMoney {
        return SendMoreMoney(genes: letters.shuffled())
    }

    func crossover(other: SendMoreMoney)
     -> (child1: SendMoreMoney, child2: SendMoreMoney) {
        let crossingPoint = Int(arc4random_uniform(UInt32(genes.count)))
        let childGenes1 = genes[0..<crossingPoint] +
         other.genes[crossingPoint..<other.genes.count]
        let childGenes2 = other.genes[0..<crossingPoint] +
         genes[crossingPoint..<genes.count]
        return (child1: SendMoreMoney(genes: Array(childGenes1)), child2:
         SendMoreMoney(genes: Array(childGenes2)))
    }

    func mutate() {
        // put a random letter in a random place
        let position1 =
         Int(arc4random_uniform(UInt32(SendMoreMoney.letters.count)))
        let position2 = Int(arc4random_uniform(UInt32(genes.count)))
        if drand48() < 0.5 { // half the time random letter
            genes[position2] = SendMoreMoney.letters[position1]
        } else { // half the time random swap
            if position1 != position2 { genes.swapAt(position1, position2) }
        }
    }

    func prettyPrint() {
        if let s = genes.index(of: "S"), let e = genes.index(of: "E"),
         let n = genes.index(of: "N"), let d = genes.index(of: "D"),
         let m =  genes.index(of: "M"), let o = genes.index(of: "O"),
         let r =  genes.index(of: "R"), let y = genes.index(of: "Y") {
            let send: Int = s * 1000 + e * 100 + n * 10 + d
            let more: Int = m * 1000 + o * 100 + r * 10 + e
            let money: Int = m * 10000 + o * 1000 + n * 100 + e * 10 + y
            print("\(send) + \(more) =
             \(money) difference:\(money - (send + more))")
        } else {
            print("Missing some letters")
        }
    }
}

The cryptarithmetic implementation shows how a problem-specific `fitness` property scores valid candidates and rejects incomplete ones. section 5.5

How Classic Computer Science Problems in Swift: Essential techniques for practicing programmers covers it

How deep it goes
Works through Swift fitness properties and score transformations, while noting bias rather than formally analyzing scoring guarantees.
Examples
Scores a two-variable equation and grades SEND+MORE=MONEY candidates by their arithmetic difference.
What it assumes
Assumes familiarity with Swift properties and arithmetic, while introducing the genetic algorithm's chromosome interface.
What you can do after
Build it from scratch by implementing a Swift `Chromosome.fitness` property that rewards better candidates and supports a stopping threshold.
How it connects
Connects fitness to chromosomes, selection, crossover, mutation, maximization, and constraint satisfaction.

Also explained in

Compare how 6 books explain fitness →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Classic Computer Science Problems in Swift: Essential techniques for practicing programmers. The section links point to the parts of the book it draws on.