Design and Analysis of Algorithms

Chapter 2
Mathematical Analysis and Correctness

Bachelors Program in AI & ML
Design and Analysis of Algorithms   •   Post Graduate

Mathematical Analysis and Correctness

Introduction

Algorithm design is not complete when an algorithm appears to work on a few examples. A professional algorithm must be shown to be correct for every valid input, and its resource requirements must be understood as the input grows. Mathematical analysis supplies the language and methods for doing both.

Correctness asks whether an algorithm always produces an output satisfying its specification. Complexity analysis asks how much time, memory, communication, or other computational resource the algorithm requires. These questions are closely related: a faster algorithm is not useful if it is incorrect, and a correct algorithm may be impractical if its resource demands are excessive.

For artificial intelligence and machine-learning systems, these issues occur in data preprocessing, graph search, optimization, indexing, model training, inference, and distributed computation. Although average empirical performance is important, formal reasoning remains essential when algorithms process large, safety-critical, adversarial, or regulated data.

Learning Objectives

By the end of this chapter, a reader should be able to:

  1. Express algorithmic resource growth using asymptotic notation.
  2. Distinguish worst-case, best-case, average-case, and amortized analysis.
  3. Establish correctness using preconditions, postconditions, invariants, induction, and termination arguments.
  4. Formulate and solve common recurrence relations.
  5. Analyze iterative, recursive, and divide-and-conquer algorithms.
  6. Recognize limitations of asymptotic analysis and select appropriate empirical measures.
  7. Apply formal analysis to an AI-related algorithmic case.

1. The Mathematical Model of an Algorithm

An algorithm is a finite, precisely specified sequence of operations that transforms input into output. A useful abstract model separates four elements:

  • Input domain: the set of permitted inputs.
  • Precondition: properties that must hold before execution.
  • Transformation: the operations performed by the algorithm.
  • Postcondition: properties guaranteed after successful termination.

For example, a sorting algorithm may have the precondition that the input is a finite sequence of comparable elements. Its postcondition is that the output is sorted and is a permutation of the input.

A specification can be written as a contract:

Precondition: A is a finite array of comparable values.
Postcondition: A is sorted in nondecreasing order and contains exactly
               the values originally present in A.

The distinction between a sorted output and a permutation is important. An algorithm that returns a sorted array containing only the smallest element is ordered but does not satisfy the full specification.

1.1 Correctness categories

An algorithm is partially correct if, whenever it terminates on a valid input, its output satisfies the postcondition. It is totally correct if it is partially correct and terminates for every valid input.

This distinction is formalized as:

{P} C {Q}

Here, P is the precondition, C is a program or algorithm, and Q is the postcondition. The Hoare-style assertion means: if P holds before executing C, and C terminates, then Q holds afterward. A termination proof is additionally required for total correctness.

2. Measuring Computational Cost

An analysis begins by defining the input-size measure n. For an array, n is usually its length. For a graph, one may use both the number of vertices V and edges E. For an integer represented in binary, the input size is the number of bits, approximately log2 x, not the numerical value x itself.

A cost model assigns unit or non-unit costs to operations. In the RAM model, arithmetic on fixed-size words, comparisons, assignments, and memory accesses are commonly treated as constant-time operations. This is an abstraction, not a physical law. Large-integer arithmetic, cache misses, parallel communication, and memory allocation may violate the unit-cost assumption.

2.1 Worst-case, best-case, and average-case analysis

Let T(I) be the cost on input I, and let |I| = n denote input size.

  • Worst-case complexity: the maximum cost among inputs of size n.
  • Best-case complexity: the minimum cost among inputs of size n.
  • Average-case complexity: the expected cost under a stated probability distribution over inputs.
  • Amortized complexity: the average cost per operation over every operation in a sequence, without requiring a probability distribution.

Average-case claims are incomplete unless the input distribution is specified. Amortized analysis is a deterministic guarantee over a sequence and should not be confused with average-case analysis.

2.2 Asymptotic notation

Asymptotic notation describes growth while suppressing machine-dependent constants and lower-order terms.

A function f(n) is O(g(n)) if there exist constants c > 0 and n0 such that, for all n ≥ n0,

0 ≤ f(n) ≤ c g(n)

It is Ω(g(n)) if there exist positive constants c and n0 such that

0 ≤ c g(n) ≤ f(n) for all n ≥ n0

It is Θ(g(n)) when both O(g(n)) and Ω(g(n)) hold. Thus, Θ gives a tight asymptotic bound, O gives an upper bound, and Ω gives a lower bound.

The notation o(g(n)) denotes a strict asymptotic upper bound: f(n) / g(n) → 0 as n → ∞. Similarly, ω(g(n)) denotes a strict lower bound. These relations are stronger than ordinary O and Ω relationships.

#### Common growth classes

ClassTypical exampleInterpretation
Θ(1)Array accessConstant growth
Θ(log n)Binary searchLogarithmic growth
Θ(n)Linear scanProportional growth
Θ(n log n)Merge sortEfficient comparison sorting
Θ(n²)Nested pair comparisonQuadratic growth
Θ(2n)Exhaustive subset searchExponential growth
Θ(n!)Exhaustive permutation searchFactorial growth

For example, 3n² + 7n + 4 = Θ(n²). The quadratic term dominates as n becomes large. However, asymptotic dominance does not imply that the quadratic algorithm is slower for every practical input: constants, cache behavior, language implementation, and input sizes matter.

2.3 Rules for analyzing programs

Sequential blocks add their costs; nested loops generally multiply iteration counts; conditional statements use the cost of the condition plus the relevant branch; recursion requires a recurrence relation.

Consider:

for i = 1 to n:
    for j = 1 to i:
        perform constant-time operation

The operation executes

∑i=1n i = n(n + 1)/2 = Θ(n²)

The exact count is n(n+1)/2, while the asymptotic time is Θ(n²). If the inner loop instead ran from 1 to n, the count would be n².

3. Recurrence Relations and Divide-and-Conquer

A recursive algorithm is often described by a recurrence. If an algorithm divides a problem of size n into a subproblems of size n/b, and performs f(n) additional work, its recurrence has the form

T(n) = aT(n/b) + f(n)

where a ≥ 1, b > 1, and f(n) is nonnegative for sufficiently large n. The base case supplies the stopping condition, commonly T(1) = Θ(1).

3.1 Substitution and recursion trees

For merge sort,

T(n) = 2T(n/2) + Θ(n)

At every recursion level, the total merging work is Θ(n). The recursion depth is log2 n, so the total is

T(n) = Θ(n log n)

A recursion-tree argument is valuable because it exposes where work occurs. At level j, there are 2j subproblems, each of size n/2j, so the total nonrecursive work remains 2j · Θ(n/2j) = Θ(n).

3.2 Master theorem

For recurrences of the form T(n) = aT(n/b) + f(n), compare f(n) with nlogb a. Under the standard regularity conditions, the Master theorem gives:

  1. If f(n) = O(nlogb a - ε) for some ε > 0, then T(n) = Θ(nlogb a).
  2. If f(n) = Θ(nlogb a logk n) for k ≥ 0, then T(n) = Θ(nlogb a logk+1 n).
  3. If f(n) = Ω(nlogb a + ε) for some ε > 0 and a regularity condition holds, then T(n) = Θ(f(n)).

The theorem does not apply automatically to arbitrary recurrences, unequal subproblem sizes, or recurrences with non-polynomial irregularities. In such cases, substitution, recursion trees, Akra–Bazzi analysis, or another method may be appropriate.

4. Proving Algorithm Correctness

A correctness proof should connect the implementation to the specification. Three techniques are especially important: mathematical induction, loop invariants, and induction on recursive structure.

4.1 Mathematical induction

To prove a proposition P(n) for every integer n ≥ n0:

  1. Prove the base case P(n0).
  2. Assume P(k) for an arbitrary k ≥ n0. This is the induction hypothesis.
  3. Prove P(k+1) using the hypothesis.

The induction variable must correspond to the structure of the claim. For divide-and-conquer algorithms, strong induction is often more natural: assume correctness for all smaller problem sizes, then prove correctness for size n.

4.2 Loop invariants

A loop invariant is a statement that remains true at a particular point in every iteration. A rigorous invariant proof has three parts:

  • Initialization: the invariant holds before the first iteration.
  • Maintenance: if it holds before an iteration, it holds after that iteration.
  • Termination: when the loop stops, the invariant and the stopping condition imply the postcondition.

An invariant must be strong enough to imply the desired result, but simple enough to prove.

4.3 Example: insertion sort

Insertion sort maintains a sorted prefix. A suitable invariant before iteration j is:

The subarray A[1 … j−1] is sorted and contains exactly the elements originally in those positions.

During the iteration, A[j] is stored as a key. Larger prefix elements are shifted right until the key can be inserted. The sorted prefix remains a permutation of its former contents plus the key, establishing maintenance. When j = n+1, the entire array is sorted and remains a permutation of the input.

If the array has n elements, insertion sort has worst-case time Θ(n²), best-case time Θ(n), and auxiliary space Θ(1), assuming in-place implementation. Its best case occurs when the input is already sorted, because each iteration performs only a constant amount of work.

4.4 Binary search: correctness and termination

Binary search assumes a sorted array and searches for a target. A useful invariant is:

If the target occurs in the array, it occurs within the current closed interval [low, high].

At each step, the midpoint is examined. If the target is smaller, the upper boundary decreases; if larger, the lower boundary increases. If equal, the algorithm returns a valid position. If low > high, the interval is empty, and the invariant implies that the target is absent.

A termination measure is high − low + 1, the number of candidate positions. Whenever the algorithm does not return, this positive integer strictly decreases. Therefore the loop terminates. The running time is Θ(log n) in the worst case and Θ(1) in the best case.

5. Termination and Ranking Functions

Correctness cannot be established by an invariant alone if an algorithm may loop forever. A ranking function maps program states to a well-founded set, usually the nonnegative integers, and strictly decreases on every nonterminating iteration.

For Euclid’s greatest-common-divisor algorithm, the pair of positive integers decreases because each remainder is smaller than the divisor. Since there cannot be an infinite descending sequence of nonnegative integers, the algorithm terminates.

Termination arguments are particularly important in iterative optimization, search with pruning, distributed protocols, and recursive procedures that process dynamically generated states. A variable that merely “usually decreases” is not a proof; the decrease must be guaranteed under the stated assumptions.

6. Amortized Analysis

Some data structures have occasional expensive operations but remain efficient over a sequence. Amortized analysis assigns a cost to the entire sequence rather than relying on probability.

Suppose a dynamic array doubles its capacity whenever it becomes full. If the array currently has capacity 2k, a resize copies 2k elements. Over n append operations, the total number of copied elements is bounded by

1 + 2 + 4 + … + 2k < 2n

Thus the total cost of n appends is O(n), giving amortized cost O(1) per append, even though one individual append may cost Θ(n).

The potential method formalizes this reasoning. Let Φ(Di) be a nonnegative potential for data-structure state Di. If operation i has actual cost ci, its amortized cost is

ĉi = ci + Φ(Di) − Φ(Di−1)

Summing over a sequence telescopes:

∑ ĉi = ∑ ci + Φ(Dm) − Φ(D0)

If Φ(Dm) ≥ 0 and Φ(D0) = 0, the total actual cost is no greater than the total amortized cost. The potential stores “credit” created by inexpensive operations and spends it during expensive operations.

7. A Worked Analysis: Dijkstra’s Algorithm

For a graph with nonnegative edge weights, Dijkstra’s algorithm computes shortest-path distances from a source vertex. Its correctness depends critically on the nonnegative-weight assumption.

The algorithm maintains a set of settled vertices whose shortest distances are final. Initially, the source has distance zero and every other vertex has distance infinity. Repeatedly, it selects the unsettled vertex with minimum tentative distance and relaxes its outgoing edges.

The central invariant is:

Every settled vertex has its true shortest-path distance from the source.

When vertex u is selected, assume for contradiction that a shorter path exists. Along that path, consider the first unsettled vertex v. Its predecessor on the path is settled, so relaxation has assigned v a tentative distance no greater than the path length. Since u has the minimum tentative distance, its distance cannot exceed the alleged shorter path. Therefore the selected distance is final. Nonnegative edges are essential: a later path through an unsettled vertex cannot reduce a distance already finalized.

With an array-based priority selection, the running time is Θ(V² + E), commonly written Θ(V²) for connected graphs with E = O(V²). With adjacency lists and a binary heap, the time is O((V+E) log V). The exact bound depends on the priority-queue implementation and whether decrease-key or duplicate entries are used.

8. Applied Case: Verifying a Nearest-Prototype Classifier

Background

Consider an illustrative AI system that classifies a new vector by assigning it the label of the closest stored prototype under squared Euclidean distance. Such a classifier can support rapid categorization of sensor records or document embeddings.

Problem

The engineering team wants assurance that the implementation returns the label of a closest prototype, handles ties deterministically, and scales predictably with the number of prototypes and feature dimensions.

Analysis

Let the query be x ∈ Rd, and let prototypes be p1, …, pm, with labels ℓ1, …, ℓm. The squared Euclidean distance is

d²(x,pi) = ∑r=1d (xr − pi,r)²

The square root is unnecessary for comparison because the square-root function is strictly increasing on nonnegative values. A linear scan computes every distance and retains the smallest distance; ties are resolved by choosing the smallest prototype index.

A loop invariant is:

After processing prototypes 1 through i, the stored candidate is the smallest-index prototype among those processed with minimum distance to x.

Initialization holds after prototype 1. During maintenance, the new prototype replaces the candidate if its distance is smaller, or if it is equal and its index is smaller. At termination, all prototypes have been processed, so the candidate satisfies the specification.

For m prototypes and d dimensions, distance computation takes Θ(d) time, and the full scan takes Θ(md) time and Θ(1) auxiliary space, excluding storage for the prototypes. If many queries are expected, spatial indexes, clustering, product quantization, or approximate-nearest-neighbor methods may reduce query time, but they introduce memory, preprocessing, approximation, and verification considerations.

Outcome and learning points

The formal proof establishes exact behavior, while the complexity analysis exposes the principal scalability bottleneck. It also shows why a reported “nearest” result is meaningful only when the distance function, numerical precision, tie policy, and data representation are specified. In production AI, reproducibility requires these details to be documented.

9. Practical and Professional Implications

9.1 Exact analysis versus empirical benchmarking

Asymptotic analysis predicts growth, whereas benchmarking measures performance on particular hardware, software, and workloads. Both are necessary. A benchmark should report input distributions, data sizes, warm-up behavior, hardware, concurrency, memory consumption, and variance. Reporting only a mean runtime can hide tail latency that matters in real-time systems.

9.2 Numerical correctness

Mathematical proofs often assume exact arithmetic. Floating-point arithmetic can violate algebraic identities through rounding, overflow, underflow, and cancellation. Algorithms involving probabilities, distances, gradients, or geometric predicates should specify tolerances and numerical-stability safeguards. A proof of logical correctness does not automatically prove numerical reliability.

9.3 Randomized algorithms

For randomized algorithms, correctness may be deterministic for every random outcome, or probabilistic. A Monte Carlo algorithm may have a small probability of returning an incorrect answer, while a Las Vegas algorithm always returns a correct answer but has random running time. Claims must identify whether probability concerns correctness, runtime, or both, and must state the probability space.

9.4 Ethical and governance considerations

Formal correctness is not equivalent to social or ethical adequacy. An algorithm can satisfy its computational specification while encoding biased data, exposing private information, or optimizing an inappropriate objective. Algorithm reviews should therefore include data provenance, fairness criteria, privacy requirements, security threats, auditability, and human oversight. Complexity optimizations must not silently remove safety checks or traceability.

10. Best Practices for Analysis and Proof

  1. State the input representation and size measure.
  2. Write the precondition and postcondition before coding.
  3. Identify the dominant operation and justify its cost.
  4. Separate exact counts from asymptotic bounds.
  5. State whether a result is worst-case, average-case, expected, or amortized.
  6. Use a clear invariant and explain initialization, maintenance, and termination.
  7. Prove termination with a decreasing well-founded measure.
  8. State assumptions such as sorted input, nonnegative weights, independence, or bounded precision.
  9. Test boundary cases, including empty, singleton, duplicate, maximum, and invalid inputs.
  10. Validate theoretical claims with reproducible experiments, without treating experiments as substitutes for proof.

Challenges and Limitations

No single analysis captures every practical concern. Asymptotic notation suppresses constants and can be uninformative for small inputs. The RAM model abstracts away memory hierarchy and parallelism. Average-case analysis depends on a distribution that may not reflect deployment. Worst-case analysis can be too pessimistic for structured workloads. Correctness proofs may apply only to an idealized specification and may omit numerical, concurrency, or systems-level failures.

For advanced systems, analysis may need to include communication complexity, external-memory transfers, energy, privacy leakage, approximation error, or fault tolerance. Consequently, algorithm evaluation should combine formal proof, complexity analysis, testing, profiling, and domain-specific validation.

Chapter Summary

Mathematical analysis and correctness provide the foundation for trustworthy algorithm design. A specification defines valid inputs and required outputs; partial correctness establishes the postcondition if execution terminates, while total correctness also requires a termination proof. Asymptotic notation describes growth using O, Ω, and Θ, but meaningful analysis requires a clear input-size measure and cost model. Recurrence relations and the Master theorem support divide-and-conquer analysis. Loop invariants, induction, and ranking functions provide systematic correctness and termination proofs. Amortized analysis explains why sequences of operations can be efficient despite occasional expensive steps. The nearest-prototype case demonstrates how proof, complexity, numerical assumptions, and AI governance interact. Formal reasoning should be complemented—not replaced—by benchmarking, testing, numerical analysis, and ethical review.

Key Terms

Precondition: A property required before an algorithm begins.

Postcondition: A property guaranteed after successful termination.

Partial correctness: Correct output is guaranteed if the algorithm terminates.

Total correctness: Partial correctness together with guaranteed termination.

Cost model: Assumptions specifying the cost of primitive operations.

Input-size measure: A definition of the size of an algorithm’s input.

Worst-case complexity: Maximum cost over inputs of a specified size.

Average-case complexity: Expected cost under a stated input distribution.

Amortized analysis: Deterministic analysis of total cost over an operation sequence.

Big-O: An asymptotic upper bound.

Big-Ω: An asymptotic lower bound.

Big-Θ: A tight asymptotic bound.

Recurrence: An equation defining a function in terms of smaller inputs.

Loop invariant: A statement preserved at a specified point in every iteration.

Ranking function: A well-founded measure that decreases to prove termination.

Induction hypothesis: The assumption used to prove the next inductive case.

Potential function: A stored measure used in amortized analysis.

Relaxation: Updating a graph distance when a shorter route is discovered.

Numerical stability: Resistance of an algorithm to errors caused by finite precision.

Specification: A formal description of permitted inputs and required behavior.

Review Questions

Short Answer Questions

  1. Distinguish partial correctness from total correctness.
  2. Why must an average-case complexity claim specify an input distribution?
  3. State the three requirements for proving a loop invariant.
  4. What is the purpose of a ranking function?
  5. Why does Dijkstra’s algorithm require nonnegative edge weights?

Long Answer Questions

  1. Explain O, Ω, and Θ notation, including a suitable example of each.
  2. Derive the time complexity of merge sort using a recursion-tree argument.
  3. Present a complete loop-invariant proof for insertion sort.
  4. Explain the potential method of amortized analysis and apply it to a dynamically expanding array.
  5. Compare theoretical analysis, benchmarking, testing, and numerical validation in AI software.

Analytical / Application Questions

  1. An algorithm satisfies T(n) = 3T(n/2) + Θ(n). Determine its asymptotic complexity and justify the result using an appropriate theorem.
  2. Design a termination measure and correctness invariant for a binary-search variant that returns the first occurrence of a duplicated target.
  3. A nearest-neighbor service uses m prototypes in d dimensions. Compare the time, memory, and correctness trade-offs of a linear scan and an approximate index, including the assumptions needed for a trustworthy evaluation.

Multiple Choice Questions with Answers

Question 1

Which statement best describes total correctness?

A. The algorithm has a tight Θ bound.

B. The algorithm terminates and satisfies its postcondition.

C. The algorithm works only on average.

D. The algorithm uses constant auxiliary space.

Correct Answer: B. The algorithm terminates and satisfies its postcondition.

Question 2

What is the tight asymptotic bound for 3n² + 7n + 4?

A. Θ(1)

B. Θ(n)

C. Θ(n²)

D. Θ(n³)

Correct Answer: C. Θ(n²)

Question 3

For T(n) = 2T(n/2) + Θ(n), what is the complexity?

A. Θ(log n)

B. Θ(n)

C. Θ(n log n)

D. Θ(n²)

Correct Answer: C. Θ(n log n)

Question 4

Which property is normally proved by loop-invariant initialization?

A. The invariant holds before the first iteration.

B. The loop has minimum runtime.

C. The input is randomly distributed.

D. The compiler removes all errors.

Correct Answer: A. The invariant holds before the first iteration.

Question 5

A ranking function used for termination must generally do what?

A. Increase without bound.

B. Remain constant.

C. Decrease in a well-founded set.

D. Depend on the programming language.

Correct Answer: C. Decrease in a well-founded set.

Question 6

What is the amortized cost of an append operation in a doubling dynamic array?

A. Θ(1)

B. Θ(log n)

C. Θ(n) for every operation

D. Θ(n²)

Correct Answer: A. Θ(1)

Question 7

If an algorithm scans m prototypes in d dimensions using squared Euclidean distance, what is its scan time?

A. Θ(m + d)

B. Θ(m/d)

C. Θ(md)

D. Θ(2^m d)

Correct Answer: C. Θ(md)

Question 8

Which assumption is essential for the standard correctness of Dijkstra’s algorithm?

A. All edge weights are identical.

B. All edge weights are nonnegative.

C. The graph is complete.

D. The graph has no cycles.

Correct Answer: B. All edge weights are nonnegative.

Question 9

What does average-case analysis require beyond an algorithm and input size?

A. A stated probability distribution over inputs.

B. A loop invariant only.

C. A factorial-time implementation.

D. A hardware-independent runtime constant.

Correct Answer: A. A stated probability distribution over inputs.

Question 10

Why can square roots be omitted when comparing Euclidean distances?

A. Square roots always produce integers.

B. The square-root function is strictly increasing on nonnegative values.

C. Distances cannot be zero.

D. The omission changes the nearest point but simplifies coding.

Correct Answer: B. The square-root function is strictly increasing on nonnegative values.

Design and Analysis of Algorithms
Chapter 2 of 2