Correctness proof


A correctness proof is a proof that shows an algorithm gives the correct output for every possible input.

Mathematical induction suits correctness proofs of recursive algorithms quite naturally. First, establish that the algorithm handles the base case(s) correctly. Then, the inductive step will involve showing that the algorithm being correct for all previous inputs (in other words, that all recursive calls work) implies that it will be correct for the next input. By the principle of mathematical induction, the algorithm is therefore proven correct for all inputs.

Consider this recursive algorithm that reverses the characters in a string:

algorithm reverse_string is
input: a string s
output: the string s reversed

if s = "" (base case)
return ""
else (recursive case)
let s' be a copy of s
remove the last character t from s'
return t + reverse_string(s')
⚠ Make sure you know what an empty string is. Here, it's written as "". Also, there's string concatenation going on in the recursive case, not addition.

Let's now prove that reverse_string is correct no matter how long a string you give it:

Theorem: reverse_string correctly returns the reverse of \(s\), for every string \(s.\)
Proof by induction: Let's perform induction on \(n\), the length of the string \(s.\)
Base case:
\(n = 0 :\) reverse_string("") returns "" \(\checkmark\)
Inductive step:
Assume reverse_string correctly returns the reverse of a string for strings of length \(k \geq 0.\) We want to show reverse_string correctly returns the reverse of a string for strings of length \(k + 1.\) Since \(k + 1 \geq 1\), reverse_string will enter its recursive case when given a string of length \(k + 1\), that is, a string of the form: $$c_1 c_2 ... c_k c_{k+1}$$ Then, it takes the last character off the string and prepends it to what the call on a string of length \(k\) returns. This looks like: \(c_{k+1}\) + reverse_string(c_1 c_2 ... c_k) According to the inductive hypothesis, this call will correctly return the reverse of a string of length \(k.\) Therefore, concatenating the last character of the string to the front of its reverse without that last character will result in the reverse of that entire string: $$c_{k+1} + (c_k ... c_2 c_1)$$ $$c_{k+1} c_k ... c_2 c_1$$ Thus, it can be concluded that if reverse_string correctly returns the reverse of a string for strings of length \(k \geq 0\), then reverse_string correctly returns the reverse of a string for strings of length \(k + 1.\) By the principle of mathematical induction, reverse_string correctly returns the reverse of a string for strings of length \(n \geq 0.\) This range encompasses every string \(s\) that could be given to reverse_string. \(■\)

Take a look at this recursive algorithm that computes the power set of a set:

algorithm power_set is
input: a set S
output: the power set of S

if S = {} (base case)
return {{}}
else (recursive case)
let S' be a copy of S
remove an element e from S'
P ← power_set(S')
let P' be a copy of P
for each set A in P'
add A ∪ {e} to P
return P
⚠ Note the use of the union operator, \(\cup.\)

The power set of the empty set is trivial. However, with each new element added to a set, the number of sets in its power set doubles. This is because each of the subsets can now include the new element or not. That is the idea this algorithm leverages to provide a recursive solution.

Is this algorithm correct though? Well, let's prove it rigorously:

Theorem: power_set correctly returns the power set of \(S\) for every finite set \(S.\)
Proof by induction: Let's perform induction on \(n\), the size of the set \(S.\)
Base case:
\(n = 0 :\) power_set({}) returns {{}} \(\checkmark\)
Inductive step:
Assume power_set correctly returns the power set of a set for sets of size \(k \geq 0.\) We want to show power_set correctly returns the power set of a set for sets of size \(k + 1.\) Since \(k + 1 \geq 1\), power_set will enter its recursive case when given a set of size \(k + 1.\) Then, it makes a call to itself to find the power set of \(S'\), which is just the given set \(S\) with \(1\) element, \(e\), removed. \(S'\) is therefore of size \(k\) since it has \(1\) fewer element. According to the inductive hypothesis, this call will correctly return the power set of a set of size \(k\): $$\mathcal{P}(S') = \set{\set{}, \set{s_1}, ... \set{s_1, ... s_k}}$$ Note that the element \(e\) is missing in all the sets in \(\mathcal{P}(S').\) What the algorithm ends up returning is a set that is effectively a union of \(\mathcal{P}(S')\) and a duplicate of \(\mathcal{P}(S')\) whose sets all include \(e\): $$\set{\set{}, \set{s_1}, ... \set{s_1, ... s_k}} \cup \set{\set{} \cup \set{e}, \set{s_1} \cup \set{e}, ... \set{s_1, ... s_k} \cup \set{e}}$$ To show this is equal to \(\mathcal{P}(S)\), we need to prove that any arbitrary subset of \(S\) is in \(\mathcal{P}(S)\) and that any arbitrary set in \(\mathcal{P}(S)\) is a subset of \(S.\)
  1. Let's begin with proving the former. It is a fact that any arbitrary subset \(X\) of \(S\) either includes \(e\) or it does not.
    • If \(e \notin X\), then the subset can be found in \(\mathcal{P}(S').\) Since \(\mathcal{P}(S)\) is created by adding elements to \(\mathcal{P}(S')\), it includes every element in \(\mathcal{P}(S')\), so our arbitrary subset will show up in \(\mathcal{P}(S).\)
    • If \(e \in X\), then there is a corresponding version of that arbitrary subset that does not include \(e\), \(X - \set{e}\), which can be found in \(\mathcal{P}(S').\) Since the algorithm operates by duplicating \(\mathcal{P}(S')\) and adding \(e\) to every set in it, our arbitrary set will show up in the final \(\mathcal{P}(S)\) as \(X - \set{e} \cup \set{e} = X.\)
    Therefore, if \(X \subseteq S\), then \(X \in \mathcal{P}(S)\), as was computed by the algorithm.
  2. Let's prove the latter. This'll be quick. Any arbitrary set \(Y\) in \(\mathcal{P}(S)\) is a subset of \(S'\), which itself is a subset of \(S\), or, it is one of the duplicated subsets that have the element \(e\), which is an element from \(S.\) There is no way for \(\mathcal{P}(S)\) to include anything not already in \(S\), as it is completely derived from \(S.\) Therefore, if \(X \in \mathcal{P}(S)\), then \(X \subseteq S.\)
  3. Having proven both ways, we can now state: \(X \subseteq S\) if and only if \(X \in \mathcal{P}(S)\), indicating that the computed power set for \(S\) is correct.
Thus, it can be concluded that if power_set correctly returns the power set of a set for sets of size \(k \geq 0\), then power_set correctly returns the power set of a set for sets of size \(k + 1.\) By the principle of mathematical induction, power_set correctly returns the power set of a set for sets of size \(n \geq 0.\) This range encompasses every set \(S\) that could be given to power_set. \(■\)
Logic & Proofs
IntegerRational numberInequalityReal numberTheoremProofStatementProof by exhaustionUniversal generalizationCounterexampleExistence proofExistential instantiationAxiomLogicTruthPropositionCompound propositionLogical operationLogical equivalenceTautologyContradictionLogic lawPredicateDomainQuantifierArgumentRule of inferenceLogical proofDirect proofProof by contrapositiveIrrational numberProof by contradictionProof by casesSummationDisjunctive normal form
Set Theory
SetElementEmpty setUniversal setSubsetPower setCartesian productStringBinary stringEmpty stringSet operationSet identitySet proof
Functions
FunctionFloor functionCeiling functionInverse function
Algorithms
AlgorithmPseudocodeCommandAsymptotic notationTime complexityAtomic operationBrute-force algorithm
Relations
RelationReflexive relationSymmetric relationTransitive relationRelation compositionEquivalence relationEquivalence class
Number Theory
Integer divisionLinear combinationDivision algorithmModular arithmeticPrime factorizationGreatest common divisorLeast common multiplePrimality testFactoring algorithmEuclid's theoremPrime number theoremEuclidean algorithm
Induction
Proof by inductionFibonacci sequenceProof by strong inductionWell-ordering principleSequenceFactorialRecursive definition
Combinatorics
Rule of productRule of sumBijection rulePermutationCombinationComplement ruleExperimentOutcomeSample spaceEventProbabilityProbability distributionUniform distributionMultisetSixfold wayInclusion-exclusion principlePigeonhole principle
Graph Theory
GraphWalkSubgraphRegular graphComplete graphEmpty graphCycle graphHypercube graphBipartite graphComponentEulerian circuitEulerian trailHamiltonian cycleHamiltonian pathTreeHuffman treeSubstringForestPath graphStarSpanning treeWeighted graphMinimum spanning treeGreedy algorithmPrim's algorithm
Recursion
RecursionRecursive algorithmCorrectness proofDivide-and-conquer algorithmSorting algorithmMerge sort