Unit 5 - Computational Complexity (Detailed)
Unit 5 - Computational Complexity (Detailed)
Computational complexity theory is a branch of theoretical computer science and mathematics that focuses on classifying computational
problems according to their inherent resource usage (difficulty) and relating these classes to each other. It seeks to understand the
fundamental limits of computation – what problems can be solved efficiently, what problems are inherently hard, and why. Unlike algorithm
analysis, which focuses on the efficiency of specific algorithms, complexity theory studies the difficulty of problems themselves, asking
about the best possible algorithm for a given problem, even if that algorithm isn't known yet.
1. Time Complexity: How much time (measured abstractly, often by the number of elementary computational steps) is required to
solve a problem instance as a function of its input size.
2. Space Complexity: How much memory (measured in bits or memory units) is required during the computation as a function of
input size.
Complexity theory provides a framework for categorizing problems, primarily distinguishing between those solvable efficiently (tractable
problems) and those for which no efficient solution is known (intractable problems).
Problem Understanding: Helps determine if finding an efficient algorithm for a problem is likely feasible or if we should seek
alternative approaches (like approximation or heuristics).
Algorithm Design Guidance: Knowing a problem's complexity class guides the search for algorithms. If a problem is known to be hard
(e.g., NP-complete), it discourages searching for an exact, efficient solution for all cases.
Cryptography: The security of modern cryptographic systems often relies on the presumed intractability of certain computational
problems (like integer factorization or discrete logarithm). Complexity theory provides the foundation for understanding this hardness.
Feasibility Limits: Establishes theoretical boundaries on what computers can achieve within reasonable time and memory constraints,
impacting fields from artificial intelligence to operations research.
Relationships Between Problems: Explores connections between different problems through reductions, showing how solving one
problem efficiently could lead to solving others efficiently.
1. Complexity Measures
What they are (Expanded Definition):
Complexity measures provide a formal way to quantify the resources (time, space) an algorithm consumes relative to the size of its input. To
make this analysis independent of specific hardware, programming languages, or compilers, we use abstract measures and focus on the
asymptotic behavior as the input size grows large.
Input Size (n): A measure of the length or magnitude of the input data. Its definition depends on the problem:
Auxiliary Space: Extra space used for temporary variables, data structures, recursion stack, etc. Often, "space complexity" refers
specifically to the auxiliary space.
Asymptotic Notations: Used to describe the limiting behavior (growth rate) of complexity functions and as ,
ignoring constant factors and lower-order terms.
Big-O (O): Asymptotic upper bound. if there exist positive constants such that for all
. "T(n) grows no faster than g(n)."
Big-Omega (Ω): Asymptotic lower bound. if there exist positive constants such that for
all . "T(n) grows at least as fast as g(n)."
Big-Theta (Θ): Asymptotic tight bound. if there exist positive constants such that
for all . "T(n) grows at the same rate as g(n)." (
).
Analysis Cases:
Worst-Case: Maximum resources used over all inputs of size . Provides an upper bound guarantee. Often the focus of complexity
analysis.
Average-Case: Expected resources used, averaged over all possible inputs of size , assuming a specific probability distribution of
inputs. Often more realistic but harder to analyze.
Best-Case: Minimum resources used over all inputs of size . Less informative generally but can identify exceptionally favorable
scenarios.
Amortized Analysis: Analyzes the average cost of operations over a sequence of operations, even if some individual operations are
expensive. Useful for data structures like dynamic arrays or hash tables.
Why are they Critically Important?
Asymptotic notations provide a robust, machine-independent way to classify algorithm scalability. They allow objective comparison of
different algorithms' fundamental efficiency for large inputs, predicting how resource usage will grow and identifying potential bottlenecks.
Focusing on worst-case complexity gives performance guarantees crucial for reliable systems.
Polynomial Time: An algorithm runs in polynomial time if its worst-case time complexity is bounded by a polynomial function of
the input size . That is, for some fixed constant .
Examples: , , , , , , .
Algorithms in this category are generally considered efficient or tractable. Although an algorithm is impractical, the key is
that the exponent is a constant, independent of . Polynomial-time algorithms scale reasonably well; doubling the input size
increases the runtime by a constant factor ( ).
Non-polynomial Time: An algorithm runs in non-polynomial time if its time complexity grows faster than any polynomial function of
.
Examples:
Exponential Time: , , .
Factorial Time: .
Superpolynomial but Sub-exponential: , .
Algorithms in this category are generally considered inefficient or intractable for large inputs. Even modest increases in can lead
to astronomical increases in runtime. For instance, in an algorithm, increasing by 1 doubles the runtime.
The polynomial/non-polynomial boundary roughly corresponds to the practical feasibility of solving problems. Problems solvable in
polynomial time can typically be solved for reasonably large inputs, while problems requiring non-polynomial time quickly become
unsolvable even for moderate input sizes due to resource limitations. This distinction forms the basis for the P versus NP problem, which
asks whether certain problems that seem to require non-polynomial time can actually be solved in polynomial time. Identifying a problem
as likely requiring non-polynomial time suggests that exact solutions might be infeasible, pushing research towards approximation
algorithms, heuristics, or solving special cases.
3. Complexity Classes P, NP, NP-hard, NP-complete
What they are (Expanded Definition):
Complexity classes group decision problems (problems with a yes/no answer) based on the computational resources required to solve them.
P (Polynomial Time):
Definition: The class of decision problems that can be solved by a deterministic algorithm in polynomial time ( for some
constant ).
Intuition: Problems in P are considered efficiently solvable or "tractable."
Examples: Is an array sorted? Is there a path from vertex to vertex in a graph? Is a number prime? (Proven in 2002).
Formal Definition (Verifier): The class of decision problems for which, if the answer is "yes," there exists a proof (called a
certificate or witness) that can be verified by a deterministic algorithm in polynomial time.
Intuition: "Easy to check." If someone gives you a potential solution, you can quickly verify if it's correct. It does not mean the
problem is "Non-Polynomial"; it refers to Non-deterministic Turing machines (a theoretical model).
Certificate Example: For the Subset Sum problem ("Is there a subset summing to T?"), if the answer is yes, the certificate is the
subset itself. Verifying involves simply summing the elements of the proposed subset and comparing to T (polynomial time).
Relationship to P: . If a problem can be solved in polynomial time, a "yes" answer can be verified in polynomial time (by
simply re-solving it; the certificate can be trivial).
Open Question: The central question in complexity theory is whether . Does "easy to check" imply "easy to solve"? Most
researchers believe , but it remains unproven.
NP-hard:
Formal Definition: A problem is NP-hard if every problem in NP can be reduced to in polynomial time ( ).
Intuition: NP-hard problems are "at least as hard as" any problem in NP. If you could solve an NP-hard problem in polynomial time,
you could use that solution (via the reduction) to solve every problem in NP in polynomial time.
Relation to NP: NP-hard problems are not necessarily in NP. They might be decision problems not verifiable in polynomial time, or
they might not even be decision problems (e.g., optimization problems like finding the shortest tour in TSP). The Halting Problem is
an example of an NP-hard problem that is not in NP (it's undecidable).
NP-complete (NPC):
Significance: If any single NP-complete problem can be solved in polynomial time, then all problems in NP can be solved in
polynomial time, meaning . Conversely, if , then no NP-complete problem can be solved in polynomial time.
First NPC Problem: The Cook-Levin theorem (1971) proved that the Circuit Satisfiability (SAT) problem is NP-complete. Other
problems are proven NP-complete by reducing a known NP-complete problem (like SAT) to them.
Why are these Classes Critically Important?
These classes provide a formal landscape for understanding problem difficulty. Identifying a problem as NP-complete is strong evidence
that it is intractable and that searching for an efficient, exact algorithm covering all cases is likely futile. This directs research efforts
towards more practical approaches for such problems. The vs question, framed by these classes, is one of the most profound open
problems in mathematics and computer science, with deep implications for computation, optimization, and artificial intelligence.
4. Polynomial-Time Reductions
What they are (Expanded Definition):
A polynomial-time reduction is a method for formally relating the difficulty of two problems. We say problem reduces to problem in
polynomial time, written , if there exists a deterministic algorithm (the reduction) that runs in polynomial time and transforms any
input instance of problem into an input instance of problem , such that the answer to on is "yes" if and only if the answer to
on is "yes."
The Process:
3. The crucial property: The solution (yes/no) for in must be the same as the solution for in .
Proving Hardness: Reductions are the primary tool for proving NP-hardness. If we know problem is NP-hard, and we can show
, then must also be NP-hard. Why? If were solvable in polynomial time, we could solve in
polynomial time (by first reducing it to and then solving ). This would imply all of NP is solvable in polynomial time.
Establishing Relationships: Reductions reveal structural similarities between problems, showing that they are computationally related
even if they appear different on the surface.
These are specific, well-studied problems proven to be NP-complete. They serve as benchmarks for hardness and are often used as starting
points for reductions.
Input: A Boolean combinatorial circuit composed of AND, OR, and NOT gates with designated inputs and a single output.
Question: Is there an assignment of TRUE/FALSE values to the inputs that makes the circuit's output TRUE?
Significance: The first problem proven NP-complete (Cook-Levin Theorem). Many other NP-completeness proofs ultimately rely on
a reduction from some variant of SAT.
3-SAT: A restricted version where the circuit is represented in Conjunctive Normal Form (CNF) with exactly three literals per clause.
Still NP-complete and often easier to reduce from.
Vertex Cover:
Question: Is there a subset of vertices such that , and every edge has at least one endpoint in (i.e.,
or )?
Context: Finding a small set of vertices that "covers" all edges. Arises in network monitoring, computational biology.
Subset Sum:
Hamiltonian Cycle: Does a graph contain a simple cycle that visits every vertex exactly once?
Traveling Salesperson Problem (Decision Version): Given a list of cities, distances between them, and a budget , is there a tour
visiting all cities exactly once with total distance ?
Graph Coloring: Given a graph and an integer , can the vertices be colored with colors such that no two adjacent vertices have the
same color?
These canonical NP-complete problems demonstrate the wide range of domains where computational intractability arises (logic, graph
theory, number theory, optimization). They provide a toolbox of known hard problems used for proving the hardness of new problems via
reduction. Understanding their structure helps in designing algorithms (approximation, heuristics) for related practical applications.
6. Randomized Algorithms
What they are (Expanded Definition):
Randomized algorithms incorporate randomness as part of their logic. Their behavior can vary on the same input across different runs,
depending on the outcomes of random choices (e.g., generated random numbers, random permutations).
Core Idea: Use random bits/numbers to make decisions within the algorithm (e.g., choosing a pivot in QuickSort, selecting a witness in
primality testing).
Types:
Las Vegas Algorithms: Always produce the correct result, but their running time varies based on random choices. The expected
running time is often analyzed. Example: Randomized QuickSort has an expected time but retains a small probability of
time.
Monte Carlo Algorithms: Run in a predetermined amount of time (often polynomial) but have a small probability of producing an
incorrect result. This error probability can usually be reduced by running the algorithm multiple times. Example: Miller-Rabin
primality test runs quickly but has a tiny chance of declaring a composite number prime (a "false positive").
Why are they Critically Important?
Simplicity: Randomized algorithms can sometimes be significantly simpler to design and analyze than their deterministic counterparts.
Efficiency: They can achieve better average-case or expected performance than the best known deterministic algorithms for certain
problems (e.g., QuickSort, primality testing). For some problems, they are the only known efficient solutions.
Avoiding Worst Cases: Randomization can help avoid worst-case behavior that might arise from specific input patterns when using
deterministic methods.
Applications: Widely used in cryptography, number theory, sampling, optimization, and parallel computing.
Complexity Classes: Randomized computation leads to classes like RP (Randomized Polynomial time - Monte Carlo with one-sided error),
ZPP (Zero-error Probabilistic Polynomial time - Las Vegas), BPP (Bounded-error Probabilistic Polynomial time - Monte Carlo with two-
sided error). . It's conjectured, but unproven, that .
7. String Matching
What it is (Expanded Definition):
The problem of finding one or all occurrences of a given pattern string (length ) within a larger text string (length ).
Naive Approach: Slide the pattern along the text . At each position, compare with the corresponding substring of .
Complexity: in the worst case (e.g., , ).
Knuth-Morris-Pratt (KMP) Algorithm: Uses information about the pattern itself to avoid redundant comparisons when a mismatch
occurs. It preprocesses the pattern to build a "failure function" (often stored in an array ) that indicates how much to shift the pattern
based on the nature of the partial match found so far.
Preprocessing Time: .
Matching Time: .
Overall Complexity: .
Rabin-Karp Algorithm: Uses hashing to compare the pattern with text substrings. It calculates a hash value for the pattern and for
each -length substring of . If the hash values match, it performs a direct character-by-character comparison to rule out "spurious hits"
(different strings having the same hash value). Uses rolling hashes to efficiently compute the hash of the next substring from the previous
one in time.
Preprocessing Time: .
Matching Time (Expected): under reasonable assumptions about the hash function.
Matching Time (Worst Case): if many spurious hits occur (unlikely with good hashing).
Boyer-Moore Algorithm: Often the fastest in practice. It compares the pattern from right to left. When a mismatch occurs, it uses two
heuristics to determine the maximum safe shift:
Bad Character Heuristic: Shift based on the last occurrence of the mismatched text character within the pattern.
Good Suffix Heuristic: Shift based on matching suffixes of the pattern that have already been successfully compared.
Complexity: in the worst case (though analysis is more complex), but often performs sub-linearly ( ) on average
for typical text.
Why is it Critically Important?
String matching is a fundamental operation in text processing, bioinformatics (sequence alignment), search engines, data retrieval, intrusion
detection, and many other areas. Efficient algorithms are crucial for handling large volumes of text data quickly.
8. Approximation Algorithms
What they are (Expanded Definition):
Algorithms designed for NP-hard optimization problems (where the goal is to find the best solution, e.g., minimum cost, maximum profit).
Since finding the exact optimal solution is believed to be intractable (requiring non-polynomial time), approximation algorithms aim to find
a provably good, though not necessarily optimal, solution in polynomial time.
Goal: Find a feasible solution whose value (cost or profit) is within a guaranteed factor of the optimal solution's value .
Example (Vertex Cover): A simple greedy algorithm (repeatedly pick an edge, add both endpoints to the cover, remove incident edges)
provides a 2-approximation for the minimum vertex cover problem. The found cover is guaranteed to be no more than twice the size of
the optimal cover.
Example (Metric TSP): For the Traveling Salesperson Problem where distances satisfy the triangle inequality (Metric TSP),
Christofides' algorithm provides a 1.5-approximation.
Approximation Schemes (PTAS/FPTAS): Some problems admit schemes that can achieve an approximation ratio of
(minimization) or (maximization) for any desired .
PTAS (Polynomial Time Approximation Scheme): Runtime is polynomial in for fixed , but may be exponential in (e.g.,
).
FPTAS (Fully Polynomial Time Approximation Scheme): Runtime is polynomial in both and (e.g., ). FPTAS are rare
and exist mainly for number-related problems like Knapsack.
For many real-world NP-hard optimization problems, finding the absolute best solution is computationally infeasible. Approximation
algorithms provide a practical alternative by delivering solutions with proven quality guarantees within reasonable (polynomial) time limits,
making them essential tools in operations research, logistics, network design, and scheduling.
9. Sorting Networks
What they are (Expanded Definition):
A sorting network is an abstract mathematical model of a network designed specifically for sorting sequences. It's constructed from a fixed
sequence of comparators. Each comparator takes two inputs and produces two outputs, sorting the inputs (i.e., outputting the minimum on
one line and the maximum on the other). The network structure is fixed and does not depend on the input values (it's data-oblivious).
Structure: Consists of "wires" carrying values and "comparators" connecting pairs of wires. The network has input wires and output
wires.
Data-Oblivious: The sequence of comparisons performed is fixed, regardless of the actual values being sorted. This makes them suitable
for hardware implementation or parallel processing where dynamic decisions are costly.
Metrics:
Size: The total number of comparators in the network. Relates to the hardware cost.
Depth: The maximum number of comparators along any path from input to output, assuming comparators at the same "time step" can
operate in parallel. Relates to parallel execution time.
Zero-One Principle: A fundamental theorem stating that if an -input comparison network correctly sorts all possible input
sequences consisting only of 0s and 1s, then it correctly sorts any sequence of arbitrary values. This greatly simplifies correctness
proofs.
Examples:
Bitonic Sorting Network: A classic construction based on merging bitonic sequences. Achieves size and depth.
AKS Sorting Network (Ajtai, Komlós, Szemerédi): A theoretical breakthrough showing networks exist with optimal depth
and size . However, the constant factors hidden by the O-notation are enormous, making it impractical.
Sorting networks provide a model for parallel sorting and are directly relevant to hardware sorting circuit design (e.g., in GPUs, network
switches). Their data-oblivious nature is advantageous in secure multi-party computation and scenarios where control flow cannot depend
on data values. The theoretical study pushes boundaries on parallel computation complexity.
Fundamental operations involving matrices, which are rectangular arrays of numbers. These operations are building blocks for countless
algorithms in scientific computing, computer graphics, machine learning, graph analysis, and more.
Basic Operations:
Addition/Subtraction: . Requires matrices to have the same dimensions. Compute for all .
Complexity: for matrices.
Standard Algorithm: Direct implementation of the definition. Complexity: for multiplying two matrices.
Simple Divide and Conquer: Partitioning into submatrices leads to 8 recursive multiplications of size . Recurrence
still yields .
Strassen's Algorithm: Uses algebraic tricks to compute the product with only 7 recursive multiplications of size .
Recurrence yields . Asymptotically faster but with larger constant factors.
Beyond Strassen: Algorithms with even better asymptotic bounds exist (e.g., Coppersmith-Winograd ), but they are
extremely complex and impractical due to massive constant factors. The theoretical lower bound is , and the exponent
is known to be between 2 and ~2.373.
Other Operations:
Matrix operations are ubiquitous in numerical methods and data analysis. Efficient matrix multiplication is particularly crucial as it
underlies many other matrix computations and complex algorithms. Improvements in matrix multiplication complexity have significant
theoretical and practical implications.
Algorithms dealing with polynomials, which are expressions involving variables raised to non-negative integer powers (e.g.,
).
Representations:
Multiplication ( ): Naive method involves convolution of coefficients. time for two degree-
polynomials.
Addition/Subtraction: If evaluated at the same points , simply add/subtract the values. time.
Multiplication: If evaluated at the same points (for degree polynomials), simply multiply the values pointwise:
. time.
The FFT Connection: Multiplication is fast ( ) in point-value form but slow ( ) in coefficient form. Converting between
representations seems slow ( using naive evaluation/interpolation). The Fast Fourier Transform (FFT) provides a breakthrough.
Discrete Fourier Transform (DFT): A specific way to evaluate a polynomial (coefficient form) at complex -th roots of unity (
). This converts coefficient to point-value representation using specific points.
Inverse DFT: Interpolates the polynomial (finds coefficients) from its values at the -th roots of unity. Converts point-value back to
coefficient representation.
FFT Algorithm: A divide-and-conquer algorithm that computes the DFT and Inverse DFT in time. It cleverly exploits
properties of the complex roots of unity.
Fast Polynomial Multiplication using FFT:
2. Evaluate (FFT): Compute point-value representations of and at the (or power of 2) roots of unity using
FFT. Cost: .
3. Pointwise Multiply: Multiply the values to get the point-value form of . Cost: .
4. Interpolate (Inverse FFT): Convert the point-value form of back to coefficient form using Inverse FFT. Cost: .
Total Time: .
Fast polynomial multiplication via FFT is a cornerstone algorithm with vast applications. It's used directly in digital signal processing
(filtering, convolution), image processing, and data compression. It also provides the basis for the fastest known algorithms for multiplying
large integers.
Extended Euclidean Algorithm: Finds integers such that . Crucial for computing modular multiplicative
inverses. Same asymptotic complexity.
Modular Arithmetic: Performing arithmetic operations (addition, subtraction, multiplication, exponentiation) within a finite system
modulo . Results "wrap around."
Modular Exponentiation: Computing . Can be done efficiently using the method of repeated squaring (also called binary
exponentiation) in modular multiplications. Essential for cryptographic protocols like RSA.
Modular Multiplicative Inverse: Finding such that . Exists if . Computable using the
Extended Euclidean Algorithm.
Trial Division: Dividing by all integers up to . Inefficient for large (exponential in the number of bits of ).
Probabilistic Tests (e.g., Fermat Test, Miller-Rabin Test): Efficient randomized algorithms. If is prime, they always output "prime".
If is composite, they output "composite" with high probability. The error probability can be made arbitrarily small by repeating the
test. Miller-Rabin runs in polynomial time (in ).
Deterministic Tests (e.g., AKS Algorithm): The AKS algorithm (2002) proved primality testing is in P, providing a deterministic
polynomial-time algorithm. However, it's generally slower in practice than Miller-Rabin for typical input sizes.
Integer Factorization: Given a composite integer , find its prime factors.
Difficulty: No known efficient (polynomial-time in ) algorithm exists for factoring large integers on classical computers. The
presumed difficulty of this problem is the foundation for the security of the RSA cryptosystem.
Algorithms: Include trial division, Pollard's rho algorithm, Quadratic Sieve (QS), and the General Number Field Sieve (GNFS).
GNFS is the fastest known classical algorithm for factoring large integers relevant to RSA, but its complexity is superpolynomial /
sub-exponential. Quantum computers, using Shor's algorithm, can factor integers in polynomial time, posing a threat to current RSA
encryption.
Chinese Remainder Theorem (CRT) is a powerful result in number theory that helps solve systems of simultaneous linear
congruences.
What is it?
Imagine you're looking for a number, let's call it . You know that when you divide by certain numbers (called moduli), you get
specific remainders. The Chinese Remainder Theorem tells you that if these moduli are pairwise coprime (meaning no two moduli
share a common factor other than 1), then there's a unique solution for within a certain range.
Theorem Statement:
Let be positive integers that are pairwise coprime.
Let be any integers.
The system of congruences:
3. Find inverses : For each , find an integer such that . This is the modular multiplicative inverse
of modulo .
4. Calculate : The solution is given by the sum:
Example:
1.
2.
3.
Here, ; ; .
The moduli are pairwise coprime.
Step 1: Calculate
.
Step 2: Calculate
.
.
.
Step 4: Calculate
remainder . (Correct)
remainder . (Correct)
remainder . (Correct)
Number theoretic algorithms form the bedrock of modern public-key cryptography (RSA, Diffie-Hellman key exchange, Elliptic Curve
Cryptography). Primality testing is essential for generating large prime numbers needed for these systems. The efficiency of GCD and
modular exponentiation makes these cryptographic operations feasible. The assumed difficulty of factorization provides their security.