# Using Induction to Design Algorithms: A Deep Dive
> **Reference**: The article discussed is "Using Induction to Design Algorithms" by Udi Manber. All inline citations refer back to that paper, using the markers provided (e.g. :contentReference[oaicite:0]{index=0}).
Designing and teaching algorithms can be guided elegantly by **mathematical induction**, traditionally used just for proving correctness. In his paper, Udi Manber explores how the entire **idea** of induction can serve as a potent lens for *creating* algorithms, not merely verifying them. Below, we break down the primary **proof techniques** and **algorithmic patterns** he showcases, with notes on how they translate from mathematics to hands-on code.
---
1. [Overview](#overview)
2. [Techniques by Proof Style](#techniques-by-proof-style)
1. [Basic Induction](#basic-induction)
2. [Strengthening the Hypothesis](#strengthening-the-hypothesis)
3. [Strong Induction (Dynamic Programming)](#strong-induction-dynamic-programming)
4. [Maximal Counterexample](#maximal-counterexample)
3. [Algorithmic Patterns](#algorithmic-patterns)
1. [Sorting-Oriented](#sorting-oriented)
2. [Searching & Elimination](#searching--elimination)
3. [Dynamic Programming](#dynamic-programming)
4. [Graph Approaches](#graph-approaches)
5. [Geometry](#geometry)
4. [Mermaid Diagrams & Examples](#mermaid-diagrams--examples)
5. [Example Table of Techniques vs. Problems](#example-table-of-techniques-vs-problems)
6. [Conclusion](#conclusion)
---
> Author: Udi Manber
> Paper: http://akira.ruc.dk/~keld/teaching/ECII_f15/Manber88.pdf
## Overview
Manber’s key premise is to **treat the design process itself** as an inductive proof. In combinatorial algorithm design, you rarely solve a large instance in one go. Instead, you pick a “simpler” subproblem (fewer elements, less structure, or partially solved states), solve that subproblem (often recursively), then grow your solution back to the full problem.
This approach yields both a blueprint for correctness *and* a blueprint for the final code. While this viewpoint may seem natural in recursion, Manber’s broader theme is that *many* algorithmic techniques — from divide and conquer to dynamic programming — can be interpreted as “variations of induction” :contentReference[oaicite:1]{index=1}.
---
## Techniques by Proof Style
### Basic Induction
**Idea**: Show a base case; then reduce the main problem of size \(n\) to a solution for \(n-1\).
- **Polynomial Evaluation**
Manber illustrates Horner’s Rule by noticing that removing either the first or last coefficient allows an inductive step. Evaluating a smaller polynomial plus a “small fix” leads to the entire solution. Specifically, the decision to factor out \(x\) (or not) influences the overall computation cost :contentReference[oaicite:2]{index=2}.
- **Intervals Containment**
Given a list of intervals, label any interval contained within another. Sorting the intervals by left endpoints (and further by right endpoints for ties) allows a near-constant-time inductive extension. Removing the “rightmost left endpoint” (or working from smallest to largest) is effectively the inductive step. Complexity drops to \(O(n \log n)\), dominated by sorting :contentReference[oaicite:3]{index=3}.
- **Finding One-to-One Mappings**
When a function \(f\) from a finite set to itself is not injective, we remove an element guaranteed *not* to belong to the largest one-to-one subset. Specifically, look for any element that has no preimage (i.e., no incoming edge in a functional digraph); it can safely be removed. This solves the problem for the reduced set by induction :contentReference[oaicite:4]{index=4}.
### Strengthening the Hypothesis
**Idea**: Make the inductive assumption stronger so that you carry extra information that simplifies the extension step.
- **Binary Tree Balance Factors**
Merely knowing all children’s balance factors is insufficient for a parent’s balance factor. You also need the **heights**. So, store both heights and balance factors at each node (the “stronger” problem). This makes it trivial to compute the parent’s balance factor once the children’s data are known :contentReference[oaicite:5]{index=5}.
- **Closest Pair of Points**
A naive divide-and-conquer can lead to \(O(n \log^2 n)\) time if you re-sort subproblems repeatedly. Instead, at every division step, *also* return the points sorted by \(y\)-coordinate as part of the inductive “solution.” Then merging sorted lists takes only \(O(n)\). This trick cuts total time to \(O(n \log n)\) :contentReference[oaicite:6]{index=6}.
### Strong Induction (Dynamic Programming)
**Idea**: Keep solutions for *all* subproblems up to size \(n\) (or up to capacity \(K\), etc.). Then any new subproblem can be solved by combining or looking up smaller results.
- **Knapsack Problem**
A naive method checks whether \(P(n, K)\) can be reduced to \(P(n-1, K)\) or \(P(n-1, K - k_n)\), but that might spawn exponential recursion. Strong induction plus a 2D table storing results for *all* \((i, j)\) with \(i \le n\) and \(j \le K\) yields an \(O(nK)\) approach. This is exactly dynamic programming: each solution is built from previously filled entries :contentReference[oaicite:7]{index=7}.
### Maximal Counterexample
**Idea**: Assume the statement is false and consider the “largest” failing example. Then show it leads to an even larger one, a contradiction.
- **Perfect Matching in Dense Graphs**
In a graph of \(2n\) vertices, each with degree \(\ge n\), a perfect matching must exist. If we assume no perfect matching, we start with a *maximal* (not maximum) matching and systematically enlarge it by a clever swap of edges. This contradiction-based argument can be turned into an algorithm: build a maximal matching quickly, then keep augmenting it until it becomes perfect :contentReference[oaicite:8]{index=8}.
---
## Algorithmic Patterns
### Sorting-Oriented
- **Interval Containment** uses sorting by endpoints to reduce checks to constant time per interval :contentReference[oaicite:9]{index=9}.
- **Topological Sorting** (also described by Manber) conceptually “removes” an in-degree zero vertex in each inductive step. Though not a numeric sort, it’s a linearization of tasks in a partial order :contentReference[oaicite:10]{index=10}.
### Searching & Elimination
- **Celebrity Problem**: If Alice knows Bob, discard Alice as a celebrity candidate; otherwise discard Bob. This single question per iteration reduces \(n\) to \(n-1\). After linear time, only one person might be a celebrity. Verify in \(O(n)\) (or even less if you track known answers). Total time is \(O(n)\) :contentReference[oaicite:11]{index=11}.
### Dynamic Programming
- **Knapsack**: The classical example of storing partial solutions in a table. This exemplifies a robust strong-induction viewpoint. The method extends to many optimization problems in combinatorics and scheduling, etc. :contentReference[oaicite:12]{index=12}.
### Graph Approaches
- **Perfect Matching**: Achieved via repeated augmentation.
- **Topological Sorting**: Again, remove an easy “base” vertex (indegree zero) and solve the smaller graph.
### Geometry
- **Closest Pair**: A shining example of divide-and-conquer augmented by sorting. The geometry-specific logic about bounding boxes (“strips”) and limited neighbors per row is a hallmark of the technique :contentReference[oaicite:13]{index=13}.
---
## Mermaid Diagrams & Examples
### Example: **Celebrity Elimination**
```mermaid
flowchart TB
A((Start)) --> Q1["Ask: Does Person A know Person B?"]
Q1 -->|Yes| discardA(Discard A from celebrity pool)
Q1 -->|No| discardB(Discard B from celebrity pool)
discardA --> NextRound1("Remaining n-1 People")
discardB --> NextRound1("Remaining n-1 People")
NextRound1 --> Q2["Ask Next Pair"]
Q2 -->|Yes| discardCandidate
Q2 -->|No| discardOther
discardCandidate --> NextRound2("Remaining n-2 People")
discardOther --> NextRound2("Remaining n-2 People")
NextRound2 --> Continue(... continue until 1 left)
Continue --> check("Verify final candidate")
check --> done((Done))
```
```