Algorithmic complexity and Big-O notation
Anchor (Master): Arora and Barak, Computational Complexity: A Modern Approach, Ch. 1; Papadimitriou, Computational Complexity, Ch. 1-2
Intuition Beginner
Imagine you are searching for a word in a book. You could start at page 1 and read every word until you find it. Or you could open to the middle, check whether the word comes before or after that point, and repeat with the relevant half. The first approach scans one page at a time. The second halves the remaining pages at each step. For a 1,000-page book, the first approach might take 1,000 steps. The second takes at most 10. For a million-page book, the first takes a million steps; the second takes 20.
This dramatic difference illustrates the power of algorithmic thinking. The two approaches solve the same problem, but one is exponentially faster than the other. The key insight is that the structure of the problem (the book is sorted alphabetically) enables a more efficient strategy (binary search) than the naive approach (linear search). Recognizing and exploiting problem structure is the essence of algorithm design.
This difference matters because it tells you whether your algorithm will finish in seconds, hours, or lifetimes. Algorithmic complexity is the study of how the running time (or memory usage) of an algorithm grows as the size of the input grows. It gives you a way to predict, before you run the code, whether your solution will be practical for the problem you are trying to solve.
Big-O notation is the language used to describe this growth. When we say an algorithm runs in time, we mean its running time grows proportionally with the input size . Double the input, and the running time roughly doubles. When we say , we mean the running time grows with the square of the input. Double the input, and the running time roughly quadruples. When we say , the running time grows logarithmically. Double the input, and the running time increases by just one step.
The difference between these growth rates becomes dramatic for large inputs. An algorithm processing a billion items takes a billion steps. An algorithm takes a quintillion steps. An algorithm takes about 30 steps. The growth rate of the algorithm matters far more than the constant factors (how fast your computer is, how efficient your code is) when the input is large enough.
Think of it this way. Suppose you have a slow computer that executes one million operations per second and a fast computer that executes one billion. Your slow computer runs an sorting algorithm on a million items, requiring about 20 million operations, finishing in 20 seconds. Your fast computer runs an sorting algorithm on the same data, requiring about one trillion operations, finishing in 1,000 seconds. The fast computer with the worse algorithm is 50 times slower than the slow computer with the better algorithm. For ten million items, the gap becomes 5,000 times. The algorithm dominates the hardware.
Big-O notation focuses on the dominant term and ignores constants. If an algorithm takes operations, we say it is because for large , the term dominates the others. The constants 3, 5, and 100 do not change the growth rate. This simplification is deliberate. It allows us to compare algorithms at a high level without getting bogged down in implementation details.
There are several common complexity classes, each describing a different growth rate. means constant time: the operation takes the same amount of time regardless of input size. Accessing an array element by index is . means logarithmic time: the running time grows slowly, adding one step each time the input doubles. Binary search is .
means linear time: the running time grows proportionally with input size. Scanning an array from start to end is . is called linearithmic time, and it is the growth rate of the best general-purpose sorting algorithms like mergesort and heapsort. means quadratic time: the running time grows with the square of the input. Comparing every element to every other element, as in simple sorting algorithms like bubble sort, is . means exponential time: the running time doubles with each additional element. Brute-force solutions to many hard problems have this growth rate.
Amortized analysis provides a way to analyze sequences of operations. Some data structures have individual operations that are expensive but rare, interspersed with many cheap operations. A dynamic array (like Python's list) doubles in size when it runs out of space. The resizing operation copies all elements, taking time. But this resizing happens only after cheap insertions. Averaging the cost over all operations, each insertion costs amortized. Amortized analysis gives a more accurate picture of performance than worst-case analysis for data structures with occasional expensive operations.
Recurrence relations describe the running time of recursive algorithms. A recurrence expresses in terms of evaluated at smaller inputs. Binary search has the recurrence , capturing the fact that it makes one recursive call on half the input and spends constant time on the comparison. Mergesort has , reflecting two recursive calls on halves of the input plus linear-time merging. Solving recurrences (using the master theorem, substitution, or recursion trees) is a core skill in algorithm analysis.
Space complexity follows the same notation but measures memory usage instead of time. An algorithm that uses an auxiliary array of size has space complexity. An algorithm that uses only a few variables regardless of input size has space complexity. Time and space are often in tension: you can sometimes reduce running time by using more memory (caching, memoization) or reduce memory usage at the cost of recomputing values.
The concept of worst-case versus average-case complexity adds nuance. An algorithm's worst-case complexity describes its performance on the hardest possible input. Its average-case complexity describes the expected performance over all inputs, often under some distribution. Quicksort has worst-case time but average-case time. For most practical purposes, quicksort is as fast as the theoretically superior mergesort, which is why it is widely used despite its worse worst case.
Best-case complexity describes the fastest the algorithm can run. For linear search, the best case is : the target is the first element. But best-case analysis is rarely useful, because it tells you nothing about typical or difficult inputs. Worst-case analysis provides a guarantee: no matter what input you encounter, the algorithm will not exceed this bound.
The power of logarithmic algorithms cannot be overstated. Logarithmic growth is so slow that for all practical input sizes, an algorithm runs effectively in constant time. For (a googol, far larger than the number of atoms in the observable universe), . This means that binary search on a sorted array of a googol elements requires at most 332 comparisons. Logarithmic algorithms are the backbone of efficient data structures: binary search trees, balanced trees (AVL, red-black), B-trees (used in databases), and skip lists all achieve operations.
Polynomial versus exponential is the most important distinction in complexity theory. Polynomial-time algorithms (, , ) are considered tractable because the running time grows at a manageable rate. Exponential-time algorithms (, ) are considered intractable because the running time becomes astronomical even for modest input sizes. The transition from polynomial to exponential is sharp: a polynomial algorithm for an NP-complete problem would render thousands of apparently intractable problems tractable overnight.
Reductions are the primary tool for relating the complexity of different problems. A polynomial-time reduction from problem A to problem B shows that B is at least as hard as A: if we could solve B efficiently, we could solve A efficiently by first reducing it to B and then solving B. NP-completeness proofs use this technique: to show that a new problem is NP-complete, reduce a known NP-complete problem to it in polynomial time. This chain of reductions, beginning with Cook's proof for SAT, has established NP-completeness for thousands of problems.
Visual Beginner
The table below shows common complexity classes and how they scale.
| Complexity | Name | ||||
|---|---|---|---|---|---|
| Constant | 1 | 1 | 1 | 1 | |
| Logarithmic | 3 | 7 | 10 | 20 | |
| Linear | 10 | 100 | 1,000 | 1,000,000 | |
| Linearithmic | 33 | 664 | 9,966 | 19,931,569 | |
| Quadratic | 100 | 10,000 | 1,000,000 | ||
| Exponential | 1,024 |
Worked example Beginner
Consider the problem of finding whether a number exists in a sorted array of numbers. Two algorithms can solve this.
Linear search examines each element in order from the first to the last. In the worst case (the target is not in the array or is the last element), it examines all elements. The worst-case complexity is .
Binary search examines the middle element, determines which half could contain the target, and recurses on that half. After each comparison, the remaining search space is halved. After comparisons, at most elements remain. The search ends when , so . The worst-case complexity is .
For , linear search performs up to 1,000,000 comparisons. Binary search performs at most 20. Binary search is 50,000 times faster.
Now consider sorting. Three algorithms solve this problem with different complexities.
Bubble sort repeatedly passes through the array, swapping adjacent elements that are out of order. After passes, the largest elements are in their final positions. The total number of comparisons is . This is .
Mergesort divides the array in half, recursively sorts each half, and merges the two sorted halves. The merge step takes time. The recursion has depth (each level halves the problem). The total time is . This can be expressed as the recurrence , which resolves to .
For , bubble sort performs about comparisons. Mergesort performs about comparisons. Mergesort is 25,000 times faster. For , the gap grows to roughly 250,000 times.
These numbers explain why algorithm choice matters more than hardware speed. A faster computer might execute operations 10 times faster. A better algorithm might execute 25,000 times fewer operations. The algorithmic improvement dwarfs the hardware improvement.
Worked example: analyzing a recursive algorithm. Consider computing the Fibonacci sequence. The naive recursive implementation makes two recursive calls for each input: . This produces a recursion tree where the number of calls doubles at each level, resulting in time. For , this takes over operations.
A memoized version stores previously computed values, avoiding redundant computation. Each Fibonacci number from 0 to is computed exactly once, giving time and space. An iterative version uses two variables and computes in time and space. The matrix exponentiation method computes in time by raising the matrix to the -th power using repeated squaring.
The progression from exponential to logarithmic time, all solving the same problem, illustrates the importance of algorithm design. The exponential algorithm is unusable for . The linear algorithm handles millions of values. The logarithmic algorithm handles astronomically large values.
Worked example: analyzing a nested loop. Consider this pseudocode for finding all pairs of elements that sum to a target value:
for i = 0 to n-1:
for j = i+1 to n-1:
if arr[i] + arr[j] == target:
output(arr[i], arr[j])The outer loop runs times. For each , the inner loop runs times. The total number of iterations is . This is time. The space complexity is since only loop variables are used.
A more efficient approach uses a hash set. For each element , check whether is in the set. If yes, we found a pair. Add to the set. This runs in expected time and space. The trade-off is additional memory for faster execution, a common pattern in algorithm design: trading space for time.
Check your understanding Beginner
Formal definition Intermediate+
Big-O notation. Let . We write if there exist positive constants and such that for all . Informally, grows no faster than a constant multiple of .
Big-Omega notation. We write if there exist positive constants and such that for all . This is the lower-bound analogue: grows at least as fast as a constant multiple of .
Big-Theta notation. We write if and . This means and grow at the same rate, up to constant factors.
Little-o notation. We write if for every positive constant , there exists such that for all . This is a strict upper bound: grows strictly slower than .
Little-omega notation. We write if for every positive constant , there exists such that for all .
Properties of asymptotic notation
Transitivity. If and , then . The same holds for , , , and .
Reflexivity. , , .
Symmetry. if and only if .
Transpose symmetry. if and only if . if and only if .
Common complexity classes
Each inclusion is strict: each class grows strictly faster than the previous one.
The master theorem
The master theorem provides a general solution for recurrences of the form , where and . This recurrence describes divide-and-conquer algorithms that split the problem into subproblems of size , spending time on the divide and combine steps.
Case 1. If for some , then .
Case 2. If for some , then .
Case 3. If for some , and if for some and large , then .
For mergesort: . Here , , . We have . This is Case 2 with : .
For binary search: . Here , , . . Case 2 with : .
For Strassen's matrix multiplication: . Here , . . Since for , this is Case 1: . Strassen's algorithm multiplies matrices faster than the naive method by reducing the number of recursive multiplications from 8 to 7 at the cost of more additions.
Amortized analysis techniques
Aggregate method. Compute the total cost of operations and divide by . For a dynamic array, insertions include resizing operations. The total cost is , so the amortized cost per insertion is .
Accounting method. Assign different "charges" to different operations. A cheap operation is charged more than its actual cost, and the surplus is stored as "credit" that pays for expensive operations later. For a dynamic array, charge each insertion 1 + 2 credit per element pays for copying.
Potential method. Define a potential function that maps the data structure state to a non-negative real number. The amortized cost of an operation is its actual cost plus the change in potential: . If the potential increases during cheap operations and decreases during expensive ones, the amortized costs are bounded. For a dynamic array, captures the excess capacity that will fund the next resize.
Key result: the sorting lower bound Intermediate+
Theorem. Any comparison-based sorting algorithm requires comparisons in the worst case to sort elements.
Proof. A comparison-based sorting algorithm can be modeled as a binary decision tree. Each internal node represents a comparison between two elements. Each leaf represents a permutation that the algorithm outputs. The algorithm must distinguish among all possible permutations of the input, because any permutation could be the correct sorted order. Therefore, the decision tree must have at least leaves.
A binary tree of height has at most leaves. Therefore:
Taking logarithms:
By Stirling's approximation, , so . More precisely:
Therefore , and any comparison-based sorting algorithm requires comparisons in the worst case.
This result establishes a fundamental limit: no comparison-based sorting algorithm can do better than in the worst case. Mergesort and heapsort achieve this bound, making them asymptotically optimal among comparison sorts.
Non-comparison sorts
Sorting algorithms that are not comparison-based can beat the bound under certain conditions. Counting sort assumes the input elements are integers in a known range and runs in time. Radix sort processes digits from least to most significant, using counting sort as a subroutine, and runs in time where is the number of digits. For -digit numbers with , radix sort runs in , which is when is constant.
These algorithms exploit additional structure in the input (bounded integer range) that comparison sorts cannot use. The trade-off is generality: comparison sorts work on any ordered data type, while counting and radix sorts require integer keys.
Bucket sort assumes that the input is uniformly distributed over a range. It divides the range into equal-sized buckets, places each element in its bucket, sorts each bucket (using insertion sort or another algorithm), and concatenates the buckets. For uniformly distributed input, each bucket contains elements on average, so the total time is . In the worst case (all elements in one bucket), bucket sort degrades to .
The practical lesson is that domain knowledge can enable more efficient algorithms. If you know your data has special structure (bounded integers, uniform distribution, nearly sorted), you can exploit it for better performance. Generic algorithms (like comparison sorts) provide guarantees for arbitrary input but may be suboptimal for specific cases.
Exercises Intermediate+
Domain evidence Master
Sorting in practice. The choice of sorting algorithm has enormous practical consequences. Python's built-in sort uses Timsort, a hybrid of mergesort and insertion sort optimized for real-world data that often contains partially ordered runs. Timsort achieves best case on already-sorted data and worst case. Java's Arrays.sort uses dual-pivot quicksort for primitives and Timsort for objects. The standard library implementations reflect decades of engineering optimization informed by complexity analysis.
Graph algorithms at scale. Google's PageRank algorithm processes the web graph (billions of nodes) using power iteration. Each iteration computes matrix-vector multiplications in time where is the number of pages and is the number of links. The complexity of Dijkstra's shortest path algorithm (using a binary heap) makes it practical for road networks with millions of nodes. For social network analysis, the Floyd-Warshall all-pairs shortest path algorithm is impractical for graphs with millions of nodes, necessitating approximate algorithms or decomposition strategies.
Database query optimization. Relational database query planners use complexity analysis to choose between execution plans. A nested-loop join of two tables with and rows takes time. A hash join takes expected time. A merge join on sorted inputs takes . For large tables, the difference between and can be the difference between hours and seconds. Query planners estimate the cost of each plan and choose the cheapest, applying the principles of algorithmic complexity to real-world data processing.
Cryptographic key sizes. The complexity of factoring algorithms determines the minimum key sizes for RSA security. The general number field sieve factors -bit integers in approximately subexponential time. This complexity estimate drives NIST's recommendations: RSA keys should be at least 2048 bits (3072 bits recommended), corresponding to the estimated difficulty of factoring with current algorithms and hardware. If a polynomial-time factoring algorithm were discovered, all RSA keys would become insecure overnight.
Advanced results Master
The complexity hierarchy theorem
The deterministic time hierarchy theorem (Hartmanis and Stearns, 1965) states that given a time-constructible function , there exist problems solvable in time that are not solvable in time on a deterministic Turing machine. This means there are problems that require more time than others: more time lets you solve more problems.
The space hierarchy theorem provides an analogous result for space. Together, these theorems establish an infinite hierarchy of complexity classes, each strictly more powerful than the previous one.
P, NP, and NP-completeness
The class P consists of all decision problems solvable in polynomial time by a deterministic Turing machine. The class NP consists of all decision problems whose yes-instances can be verified in polynomial time given a certificate. Equivalently, NP is the class of problems solvable in polynomial time by a nondeterministic Turing machine.
NP-completeness identifies the hardest problems in NP. A problem is NP-complete if (1) , and (2) every problem in NP is polynomial-time reducible to . Cook's 1971 theorem established that SAT (Boolean satisfiability) is NP-complete. Karp's 1972 paper showed that 21 other problems are NP-complete by reduction from SAT.
If any NP-complete problem has a polynomial-time algorithm, then P = NP. Whether P equals NP remains the most important open question in theoretical computer science. Most researchers believe P != NP, but a proof has remained elusive for over fifty years.
Beyond NP: PSPACE and EXPTIME
PSPACE is the class of problems solvable in polynomial space. PSPACE contains NP (since polynomial time implies polynomial space) and is believed to strictly contain it. PSPACE-complete problems, like generalized chess and Go, require polynomial space but may need exponential time.
EXPTIME is the class of problems solvable in exponential time. By the time hierarchy theorem, EXPTIME strictly contains P. Problems like deciding the winner in a generalized game of chess on an board are EXPTIME-complete.
Circuit complexity
Boolean circuits provide an alternative model of computation. A Boolean circuit with inputs and one output computes a Boolean function . The circuit complexity of is the size (number of gates) of the smallest circuit computing .
A long-standing open problem is whether there exist functions in NP that require super-polynomial circuit size. If such functions exist, then P != NP. Shannon's counting argument shows that most Boolean functions require exponential circuit size (), but explicit constructions of hard functions remain elusive.
Communication complexity
Communication complexity, introduced by Yao in 1979, studies the minimum number of bits two parties must exchange to compute a function of their combined inputs. Each party holds part of the input, and they communicate to compute the output.
Communication complexity lower bounds have been used to prove lower bounds in many other models, including circuit depth, data structure query time, streaming algorithms, and extended formulations for linear programming. The method of "lifting" converts communication lower bounds into lower bounds for other models, providing a unified framework for proving hardness results.
Fine-grained complexity
Fine-grained complexity studies the exact complexity of problems within P. Rather than asking whether a problem is in P or not, it asks whether a problem can be solved in, say, time for some , or whether the known algorithm is optimal.
The Strong Exponential Time Hypothesis (SETH), formulated by Impagliazzo and Paturi in 2001, conjectures that SAT cannot be solved in time for any . SETH has been used to show tight lower bounds for many problems in P: edit distance cannot be solved in , longest common subsequence cannot be solved in , and many other problems have similar conditional lower bounds.
Parameterized complexity
Parameterized complexity studies the complexity of problems relative to a parameter in addition to the input size . A problem is fixed-parameter tractable (FPT) if it can be solved in time for some function and constant . The key insight is that for small values of , FPT algorithms are efficient even for large .
The W-hierarchy (W[1], W[2], ...) captures problems believed not to be FPT. Showing that a problem is W[1]-hard provides evidence that it is not fixed-parameter tractable, analogous to how NP-hardness provides evidence that a problem is not polynomial-time solvable.
A classic FPT problem is vertex cover: given a graph and parameter , does have a vertex cover of size at most ? A bounded search tree algorithm solves this in time by branching on each edge: at least one endpoint must be in the cover, so we try both and recurse. For and , this takes about operations, which is feasible. The brute-force approach (checking all subsets of size ) takes time, which for and is , completely infeasible. The FPT algorithm's running time depends polynomially on and exponentially only on , making it practical for small even when is very large.
Average-case complexity
Average-case complexity studies the expected running time of algorithms over a distribution of inputs. Leonid Levin defined the notion of average-case completeness: problems that are hard on average (not just in the worst case) under specific distributions. This framework is important for cryptography, where security requires that problems be hard not just for some inputs but for randomly chosen inputs.
The smoothed analysis framework (Spielman and Teng, 2001) bridges worst-case and average-case analysis. Instead of asking about the worst input or the average input, smoothed analysis asks about the worst input after a small random perturbation. Formally, the smoothed complexity of an algorithm is , where is Gaussian noise with standard deviation . For the simplex method, Spielman and Teng showed that the smoothed complexity is polynomial for any , explaining why simplex is fast in practice despite its exponential worst case.
Online algorithms and competitive analysis
Online algorithms must make decisions without knowing future inputs. A caching algorithm must decide which item to evict without knowing which items will be requested next. A load balancing algorithm must assign jobs to servers without knowing future job arrivals.
Competitive analysis measures the performance of an online algorithm against the optimal offline algorithm that knows the entire input in advance. An online algorithm is -competitive if its cost is at most times the optimal cost for every input sequence. The LRU (Least Recently Used) caching algorithm, which evicts the least recently accessed item, is -competitive where is the cache size. No deterministic online caching algorithm can do better than -competitive, so LRU is optimal among deterministic strategies.
Connections Master
Connections to mathematics and number theory
Complexity theory has deep connections to number theory. The AKS primality test (2002) placed primality testing in P, resolving a long-standing question. Integer factorization, the basis of RSA encryption, is in NP but is not known to be NP-complete. Its suspected intermediate status (neither in P nor NP-complete) connects to the study of NP-intermediate problems under the assumption P != NP (Ladner's theorem).
Complexity theory also connects to algebraic geometry through the study of arithmetic circuits and the VP vs VNP question, an algebraic analogue of P vs NP. The permanent of a matrix, which is #P-complete (harder than any NP problem), connects to combinatorics and representation theory.
Connections to cryptography
The security of cryptographic systems depends on complexity assumptions. RSA assumes integer factorization is hard. Diffie-Hellman assumes the discrete logarithm problem is hard. Post-quantum cryptography (lattice-based, code-based, hash-based) assumes certain problems remain hard even for quantum computers.
The relationship between complexity and cryptography is fundamental: cryptographic security requires that certain problems be computationally hard, while efficiency requires that related problems be easy. Breaking RSA (factoring) should be hard; using RSA (modular exponentiation) should be easy. This asymmetry is the foundation of public-key cryptography.
One-way functions, which are easy to compute but hard to invert, are the theoretical foundation of cryptography. A function is one-way if can be computed in polynomial time, but for a randomly chosen in the range of , finding any such that cannot be done in polynomial time with non-negligible probability. The existence of one-way functions implies P != NP, but the converse is not known. Candidate one-way functions include integer multiplication (factoring is believed hard), discrete exponentiation (discrete logarithm is believed hard), and cryptographic hash functions.
Connections to physics
The relationship between computational complexity and physics has deepened considerably. Quantum computing challenges the classical complexity hierarchy: Shor's algorithm factors integers in polynomial time on a quantum computer, and Grover's algorithm searches an unsorted database in time. The class BQP (bounded-error quantum polynomial time) contains problems solvable efficiently by quantum computers. Whether BQP strictly contains P is an open question.
The connections go deeper. Black hole physics (the firewall paradox) has been related to computational complexity through the complexity=action and complexity=volume conjectures. The holographic principle relates the complexity of quantum states to geometric properties of spacetime.
The relationship between thermodynamics and computation is also fundamental. Landauer's principle (1961) establishes a physical lower bound on the energy required to erase one bit of information: , where is Boltzmann's constant and is temperature. This connects information theory, computation, and physics: there are fundamental physical limits to how efficiently computation can be performed. Reversible computation (which does not erase information) can theoretically operate with zero energy dissipation, but irreversible operations have a minimum energy cost dictated by thermodynamics.
Connections to optimization
Many optimization problems in operations research, logistics, and engineering are NP-hard. Integer programming, the traveling salesman problem, scheduling, and facility location all lack known polynomial-time algorithms. Approximation algorithms and heuristics provide practical solutions with provable or empirical quality guarantees.
Linear programming, by contrast, is solvable in polynomial time (via the ellipsoid method or interior point methods). This dichotomy between linear and integer programming illustrates the sharp boundary between tractable and intractable problems.
The simplex method, developed by George Dantzig in 1947, solves linear programs by walking along vertices of the feasible polyhedron. In the worst case, it visits an exponential number of vertices, but in practice it is remarkably fast. Interior point methods, developed by Karmarkar in 1984, provably solve linear programs in polynomial time and are competitive with simplex on many practical instances. The contrast between simplex (exponential worst case, fast in practice) and interior point (polynomial worst case) illustrates why worst-case complexity is only part of the story.
Connections to software engineering
Algorithmic complexity directly affects software performance. A developer who uses a bubble sort () instead of a built-in sort () on a million-element list creates code that is 50,000 times slower. Understanding complexity helps developers choose appropriate data structures (hash tables for lookups vs. sorted arrays for binary search), design efficient algorithms, and predict whether their code will scale.
Profiling tools measure where programs spend their time, but complexity analysis predicts whether a program will scale before it is written. A startup building a social network with 1,000 users might not notice that their friend recommendation algorithm is . At 1 million users, the algorithm becomes 10^12 times slower, turning a 1-second computation into a 30-year computation. Understanding complexity prevents these scaling disasters.
Historical and philosophical context Master
The origins of asymptotic analysis
The formal study of algorithm efficiency began with the analysis of sorting algorithms in the 1950s and 1960s. Before Big-O notation, algorithms were compared empirically: run them and measure the time. This approach is unreliable because results depend on the hardware, the implementation, and the specific inputs chosen.
Paul Bachmann introduced Big-O notation in 1894 in the context of analytic number theory. Edmund Landau adopted and popularized it. Donald Knuth extended the notation to include and in his 1976 article "Big Omicron and Big Omega and Big Theta," standardizing the terminology used today. Knuth chose to use the Greek letter Omicron () for the upper bound because it matched the traditional mathematical notation, and he introduced (Omega) for lower bounds and (Theta) for tight bounds.
The Hartmanis-Stearns hierarchy
Juris Hartmanis and Richard Stearns published their hierarchy theorems in 1965, establishing that giving a Turing machine more time (or space) strictly increases the class of problems it can solve. This was one of the first results in what became the field of computational complexity theory, and it earned them the 1993 Turing Award.
Their work showed that the intuitive notion that "more resources let you solve more problems" could be made mathematically precise. The time hierarchy theorem shows that there exist problems solvable in time that are not solvable in time. More generally, for any time-constructible function , there are problems requiring time that cannot be solved in time.
Cook, Karp, and the P versus NP question
Stephen Cook's 1971 paper "The Complexity of Theorem-Proving Procedures" introduced the concept of NP-completeness and showed that SAT is NP-complete. Richard Karp's 1972 paper "Reducibility among Combinatorial Problems" showed that 21 diverse problems are all NP-complete, establishing the breadth and importance of the concept.
Leonid Levin, working independently in the Soviet Union, discovered similar results around the same time. The Cook-Levin theorem is thus named for both researchers.
The P versus NP question was formally posed in these papers, though the concept was foreshadowed by Kurt Godel in a 1956 letter to John von Neumann, where Godel asked whether theorem-proving could be done in linear or quadratic time. Von Neumann, already ill with cancer, did not reply.
The philosophical significance of complexity
Computational complexity raises philosophical questions about the nature of mathematical knowledge. A proof that P != NP would establish that there are problems where verifying a solution is fundamentally easier than finding one. This would formalize the intuition that creativity (finding solutions) is harder than appreciation (recognizing correct solutions).
The question also touches on the nature of creativity itself. If P = NP, then any problem whose solution can be efficiently verified can also be efficiently solved. This would mean that finding mathematical proofs, composing music, writing novels, and designing engineering solutions could all be automated. The societal implications would be transformative and, for many, deeply unsettling.
Scott Aaronson has argued that P versus NP is not merely a technical question but a question about the nature of mathematical reality. If P != NP, it confirms that the universe contains intrinsic computational barriers that no amount of cleverness can overcome.
The practical impact of complexity theory
Complexity theory has had enormous practical impact beyond theoretical computer science. The theory of NP-completeness gives engineers a precise language for explaining why certain problems are hard: when a problem is NP-hard, no efficient algorithm is known, and finding one would resolve the P versus NP question. This shifts the focus from "find a polynomial algorithm" to "find a good approximation" or "find an algorithm that works well in practice."
Approximation algorithms provide provably near-optimal solutions to NP-hard optimization problems. The vertex cover problem, while NP-hard, has a simple 2-approximation: find a maximal matching and take both endpoints of each matched edge. This algorithm runs in polynomial time and produces a solution at most twice the optimal size. The PCP theorem (Arora et al., 1998) showed that many NP-hard problems cannot be approximated beyond certain thresholds, establishing limits on what polynomial-time algorithms can achieve.
Randomized algorithms use randomness to achieve efficiency that deterministic algorithms cannot match (or are not known to match). The Miller-Rabin primality test runs in time for iterations, with probability of error at most . For practical purposes (say ), this gives a deterministic-running, extremely reliable primality test that is much faster than the deterministic AKS algorithm. The probabilistic method in combinatorics, pioneered by Paul Erdos, uses randomness to prove the existence of combinatorial objects with desired properties, even when constructing them deterministically is difficult.
Las Vegas algorithms always produce correct results but have random running time. Quicksort with random pivot selection is a Las Vegas algorithm: it always sorts correctly, but its running time varies. Monte Carlo algorithms have deterministic running time but may produce incorrect results with bounded probability. The Miller-Rabin primality test is a Monte Carlo algorithm: it runs in deterministic time but has a small probability of incorrectly reporting a composite number as prime. The choice between Las Vegas and Monte Carlo depends on the application: for problems where correctness is critical (cryptographic key generation), Las Vegas is preferred. For problems where speed is critical and approximate answers are acceptable (Monte Carlo integration), Monte Carlo is preferred.
Complexity and machine learning
Machine learning raises new questions for complexity theory. Training neural networks is NP-hard in the worst case (Blum and Rivest, 1993), yet gradient descent finds good solutions in practice. This gap between worst-case theory and empirical practice is an active area of research. Smoothed analysis (Spielman and Teng, 2001), which won the 2008 Godel Prize, provides a partial explanation: it shows that for many problems, worst-case instances are fragile and small random perturbations make them tractable. This framework explains why algorithms that are slow in theory (like the simplex method for linear programming) are fast in practice.
Bibliography Master
Primary sources
- Cook, S.A. (1971). "The complexity of theorem-proving procedures." Proceedings of the 3rd ACM Symposium on Theory of Computing, 151-158.
- Karp, R.M. (1972). "Reducibility among combinatorial problems." In Complexity of Computer Computations, 85-103.
- Hartmanis, J. and Stearns, R.E. (1965). "On the computational complexity of algorithms." Transactions of the American Mathematical Society, 117, 285-306.
- Knuth, D.E. (1976). "Big omicron and big omega and big theta." ACM SIGACT News, 8(2), 18-24.
Secondary sources
- Arora, S. and Barak, B. (2009). Computational Complexity: A Modern Approach. Cambridge University Press.
- Papadimitriou, C.H. (1994). Computational Complexity. Addison-Wesley.
- Cormen, T.H., Leiserson, C.E., Rivest, R.L., and Stein, C. (2009). Introduction to Algorithms (3rd ed.). MIT Press.
- Sipser, M. (2012). Introduction to the Theory of Computation (3rd ed.). Cengage Learning.
- Aaronson, S. (2016). "P ?= NP." In Open Problems in Mathematics, Springer.
- Impagliazzo, R. and Paturi, R. (2001). "On the complexity of k-SAT." Journal of Computer and System Sciences, 62(2), 367-375.
- Downey, R.G. and Fellows, M.R. (2013). Fundamentals of Parameterized Complexity. Springer.
- Kushilevitz, E. and Nisan, N. (1997). Communication Complexity. Cambridge University Press.
- Aaronson, S. (2013). Quantum Computing since Democritus. Cambridge University Press.
- Arora, S. and Barak, B. (2009). Computational Complexity: A Modern Approach. Cambridge University Press.