Algorithm Analysis

Created

Computational Tractability

Brute Force

Brute Force: for many nontrivial probelms, there is a natural brute-force search algorithm that checks every possible solution.

  • Typically takes 2n2^n steps (or worse) for inputs of size nn.
  • Unacceptable in practice.

For the stable matching problem: test all n!n! perfect matchings for stability.

Polynomial Running Time

Desirable scaling property: when the input size doubles, the algoirhtm should slow down by at most some multiplicative constant factor CC.

An algorithm is poly-time if the above scaling property holds:

  • There exists constants a>0a > 0 and b>0b >0 such that, for every input of size nn, the algorithm performs ≤a\leq a nbn^b primitive computational steps (corresponds to C=2bC=2^b).

This doesn't have the desireable scaling property: f(n)=n!f(n) = n! f(2n)=(2n)!=C×n!f(2n) = (2n)! = C \times n!

These have the desireable scaling property: f(n)=anbf(n) = a n^b f(2n)=a(2n)b=2b×f(n)f(2n) = a(2n)^b = 2^b \times f(n) For example, f(n)=10n3f(n) = 10 n^3 f(2n)=80n3=8×f(n)f(2n) = 80n^3 = 8 \times f(n) And, f(n)=3n2+10n+7f(n)=3n^2 + 10n + 7 f(2n)=12n2+20n+7≤4×f(n)=12n2+40n+28f(2n) = 12n^2 + 20n + 7 \leq 4 \times f(n) = 12n^2 + 40n + 28

We say that an algorithm is efficient if it has a polynomial running time. Theory: Definition is (relatively) insensitive to model of computation In practice, it really works because

  • the poly-time algorithms that people develop have both small constants and small exponents.
  • breaking through the exponential barrier of brute force typically exposes some crucial structure of the problem.

The exception is that some poly-time algorithms in the wild have galactic constants and/or huge exponents.

Worst-case Analysis

Worst case: the running time guarantee for any input of size nn.

  • This generally represents efficiency in practice
  • Draconian (excessively harsh) view, but hard to find effecive alternative

Exceptions: some exponential-time algorithms are used widely in practice because the worst-case instsances don't arise

Other types of analyses

Probabilistic: expected running time of a randomized algorithm.

  • Ex: the expected number of compares to quicksort nn elements is ~2nln⁡n2n \ln n

Amortized: worst-case running time for any sequence of nn operations

  • Ex: starting from an empty stack, any sequence of nn push and pop operations takes O(n)O(n) primitive computational steps using a resizing array.

There is also: average-case analysis, smoothed analysis, competitive anlysis

Asymptotic Order of Growth

Big O Notation

Represents the upper bounds. f(n)f(n) is O(g(n))O(g(n)) if there exist constants c>0c > 0 and n0≥0n_0 \geq 0 such that 0≤f(n)≤c⋅g(n)0 \leq f(n) \leq c \cdot g(n) for all n≥n0n \geq n_0.

Ex: f(n)=32n2+17n+1f(n) = 32n^2 + 17n + 1

  • f(n)f(n) is O(n2)O(n^2)
  • f(n)f(n) is neither O(n)O(n) nor O(nlog⁡n)O(n \log n) because there does not exist constants CC, n0n_0 such that 32n2+17n+1≤C⋅n32n^2 + 17n + 1 \leq C \cdot n, ∀n≥n0\forall n \geq n_0

Typical usage: insertion sort makes O(n2)O(n^2) compares to sort nn elements.

One-way "equality" O(g(n))O(g(n)) is a set of functions, but computer scientists often write f(n)=O(g(n))f(n)=O(g(n)) instead of f(n)∈O(g(n))f(n) \in O(g(n)).

  • Ex: consider g1(n)=5n3g_1(n)=5n^3 and g2(n)=3n2g_2(n) = 3n^2
    • we have g1(n)=O(n3)g_1(n)=O(n^3) and g2(n)=O(n3)g_2(n)=O(n^3), but we cannot conclude that g1(n)=g2(n)g_1(n)=g_2(n)

Domain and codomain: ff and gg are real-valued functions.

  • The domain is typically the natural numbers N→R\mathbb{N} \to \mathbb{R} where N\mathbb{N} represent the input size, recurrence relations.
  • Sometimes we extend to the reals: R≥0→R\mathbb{R}_{\geq 0} \to \mathbb{R} where R≥0\mathbb{R}_{\geq 0} represent plotting, limits, calculus
  • Or restrict to a subset.

The bottom line is that it is okay to abuse notation in this way, but it is not okay to misuse it.

Properties

Reflexivity: ff is O(f)O(f) Constants: If ff is O(g)O(g) and c<0c<0 then cfcf is O(g)O(g). Products: If f1f_1 is O(g1)O(g_1) and f2f_2 is O(g2)O(g_2), then f1f2f_1 f_2 is O(g1g2)O(g_1g_2). The proof is as follows: - ∃c1>0\exists c_1 > 0 and n1≥0n_1 \geq 0 such that 0≤f1(n)≤c1⋅g1(n)0 \leq f_1(n) \leq c_1 \cdot g_1(n) for all n≥n1n \geq n_1. - ∃c2>0\exists c_2 > 0 and n2≥0n_2 \geq 0 such that 0≤f2(n)≤c2⋅g2(n)0 \leq f_2(n) \leq c_2 \cdot g_2(n) for all n≥n2n \geq n_2. - Then, 0≤f1(n)⋅f2(n)≤c1⋅c2⋅g1(n)⋅g2(n)0 \leq f_1(n) \cdot f_2 (n) \leq c_1 \cdot c_2 \cdot g_1(n) \cdot g_2(n) for all n≥max{n1,n2}n \geq max \{ n_1, n_2\}. ■\blacksquare

Sums: If f1f_1 is O(g1)O(g_1) and f2f_2 is O(g2)O(g_2), then f1=f2f_1 = f_2 is O(max{g1,g2})O(max \{g_1, g_2\}) (the max will ignore lower-order terms).

  • Ex: f1=100n10=O(n10)f_1 = 100n^{10} = O(n^{10}) and f2=51n5=O(n5)f_2 = 51n^5 = O(n^5), so f1+f2=O(n10)f_1 + f_2 = O(n^{10}).

Transitivity: if ff is O(g)O(g) and gg is O(h)O(h), then ff is O(h)O(h).

Big Omega Notation

Represents the lower bounds. f(n)f(n) is Ω(g(n))\Omega (g(n)) if there exists constants c>0c >0 and n0≥0n_0 \geq 0 such that f(n)≥c⋅g(n)≥0f(n) \geq c \cdot g(n) \geq 0 for all n≥n0n \geq n_0.

Ex: f(n)=32n2+17n+1f(n) = 32 n^2 + 17n + 1.

  • f(n)f(n) is both Ω(n2)\Omega(n^2) and Ω(n)\Omega(n), but is not Ω(n3)\Omega(n^3).
  • 32m2+17m+1≥1⋅n32m^2 + 17m + 1 \geq 1 \cdot n, ∀n≥1\forall n \geq 1 when c=1,n0=1c = 1, n_0 = 1.

Typically used for any compare-based sorting algorithm that requires Ω(nlog⁡n)\Omega(n \log n) compares in the worst case.

Bit Theta Notation

Represents the tight bounds. f(n)f(n) is Θ(g(n))\Theta(g(n)) if there exists constants c1>0c_1 > 0, c2>0c_2 >0 and n0≥0n_0 \geq 0 such that 0≤c1⋅g(n0)≤f(n)≤c2⋅g(n)0 \leq c_1 \cdot g(n0) \leq f(n) \leq c_2 \cdot g(n) for all n≥n0n\geq n_0.

Example: f(n)=32n2+17n+1f(n) = 32n^2+17n+1.

  • f(n)=Θ(n2)f(n) = \Theta(n^2) (choose c1=32,c2=50,n0=1)c_1 = 32, c_2 =50, n_0=1)
  • f(n)f(n) is neither Θ(n)\Theta(n) nor Θ(n3)\Theta (n^3)

Typical usage: merge sort makes Θ(nlog⁡n)\Theta(n\log n) compares to sort nn elements.

Asymptotic Bounds

Proposition: If limn→∞f(n)g(n)=clim_{n \to \infty} \frac{f(n)}{g(n)} = c for some constant 0<c<∞0<c<\infty then f(n)f(n) is Θ(g(n))\Theta(g(n)). Proof:

  • By definition of the limit, for any ε>0\varepsilon > 0, there exists n0n_0 such that c−ϵ≤f(n)g(n)≤c+ϵc - \epsilon \leq \frac{f(n)}{g(n)} \leq c + \epsilon for all n≥n0n \geq n_0.
  • Choose ε=12c>0\varepsilon = \frac{1}{2}c > 0.
  • Multiplying by g(n)g(n) yields 12c⋅g(n)≤f(n)≤32c⋅g(n)\frac{1}{2} c \cdot g(n) \leq f(n) \leq \frac{3}{2} c \cdot g(n) for all n≥n0n\geq n_0,
  • Thus, f(n)f(n) is Θ(g(n))\Theta(g(n)) by definition, with c1=12cc_1 = \frac{1}{2}c and c2=32cc_2=\frac{3}{2}c

Proposition: If limn→∞f(n)g(n)=0lim_{n \to \infty} \frac{f(n)}{g(n)} = 0, then f(n)f(n) is O(g(n))O(g(n)) but not Ω(g(n))\Omega(g(n)). Proposition: If limn→∞f(n)g(n)=∞lim_{n \to \infty} \frac{f(n)}{g(n)} = \infty , then f(n)f(n) is Ω(g(n))\Omega(g(n)) but not O(g(n))O(g(n)).

The asymtomatic bounds for some common functions:

Polynomials: Let f(n)=a0+a1n+⋯+adndf(n) = a_0 + a_1n + \dots + a_dn^d with ad>0a_d > 0. Then, f(n)f(n) is Θ(nd)\Theta(n^d). Proof:

  • limn→∞a0+a1n+⋯+adndnd=ad>0lim_{n\to\infty}\frac{a_0+a_1n+\dots+a_dn^d}{n^d} = a_d > 0

Logarithms: log⁡an\log_a n is Θ(logbn)\Theta(log_b n) (log base does not need to be specified if it is a constant) for every a>1a > 1 and every b>1b > 1. Proof:

  • log⁡anlog⁡bn=1logba\frac{\log_a n}{\log_b n} = \frac{1}{log_b a}

Logarithms and polynomials: log⁡an\log_a n is O(nd)O(n^d) for every a>1a > 1 and every d>0d>0 Proof:

  • limn→∞log⁡annd=0lim_{n \to \infty} \frac{\log_a n}{n^d} = 0

Exponentials and polynomials: ndn^d is O(rn)O(r^n) for every r>1r > 1 and every d>0d>0 Proof:

  • limn→∞ndrn=0lim_{n \to \infty} \frac{n^d}{r^n} = 0

Factorials: n!n! is 2Θ(nlog⁡n)2^{\Theta (n \log n)}. Proof:

  • Stirling's formula says n!∼2πn)(ne)nn! \sim \sqrt{2\pi n})(\frac{n}{e})^n

Algorithm Analysis

We want to implement the gale-shapley to run in O(n2)O(n^2) time.

  • To represent hospitals and students we index them 1,…,n1, \dots, n.
  • To represent a matching, we maintain two arrays student[h]student[h] and hospital[s]hospital[s]. When hh is matched with ss, then student[h]=sstudent[h] = s and hospital[s]=hhospital[s]=h. Use the value 00 to designate that hospital or student is unmatched.
    • Can add/remove a pair from matching in O(1)O(1) time.
    • Maintain a set of unmatched hospitals in a queue (or stack)
    • Can find an unmatched hospital in O(1)O(1) time.

Hospital makes a proposal data representation

  • key operaion: find hospital's next favorite student
  • for each hospital: maintain a list of students, ordered by preference
  • for each hospital: maintain a pointer to student for next proposal.
  • making a proposal takes O(1) time.

Student accepts/rejects a proposal data representation

  • does student ss perfer hosiptal hh to hospital h′h'?
  • for each studnet, create inverse of preference list of hospitals. (a ranking list).
  • after Θ(n2)\Theta(n^2) preprocessing time (to create the nn ranking arrays), it takes O(1)O(1) time to accept/reject a proposal.

Theorem: we can implement Gale-Shapley to run in O(n2)O(n^2) time. Proof:

  • Θ(n2)\Theta(n^2) preprocessing time to create nn ranking arrays.
  • There are O(n2)O(n^2) proposals; processing each proprosla takes O(1)O(1) time.

Common Running Times

Constant time has running time of O(1)O(1).

  • this means that the runtime does not depend on the input size nn.
  • e.g., conditional branch, arithmetic/logic operation, declare/initialize variable

Linear time has running time of O(n)O(n).

  • e.g., merge two sorted list.

Logarithmic time has running time of O(log⁡n)O(\log n).

  • e.g., search in a sorted array, binary search

Linearithmic time has running time of O(nlog⁡n)O(n \log n).

  • e.g., given an array of nn elements, rearrange them in ascending order, mergesort

Quadratic time has running time of O(n2)O(n^2).

  • e.g.,: closest pair of points. Given a list of nn points in a plane (x1,x2),…,(xn,yn)(x_1, x_2), \dots, (x_n,y_n), find the pair of points that is closest to each other; enumerate all pairs of points.

Cubic time: running time is O(n3)O(n^3).

  • e.g., 3 sym, enumerate all triples

Polynomial time: running tim eis O(nk)O(n^k) for some constant k>0k >0.

  • E.g., Independent set of size kk (a constant). Given a graph, find kk nodes such that no two are joined by an edge; enuermate all subsets of kk nodes, the number k-elemet subsets is n-choose-k.

Exponential time: running time is O(2nk)O(2^{n^k}) for some constant k>0k>0.

  • e.g., euclidean TSP (given nn points in th eplane, find a tour of minimum length); O(n×n!)O(n \times n!) algorithm. Enumerate all permutations of length n.