Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Everything later in this course β€” solving Bellman equations, nesting a solver inside a likelihood, finding all equilibria of a game β€” is limited by how fast the inner loop runs. This class is about what makes an algorithm fast, and how to tell before you write it.

Writing programs that work fastΒΆ

An algorithm is a method of solving a class of problems on a computer β€” a sequence of steps/commands for the computer to run.

Relevant questions:

  1. How much time does it take to run?

  2. How much memory does it need?

  3. What other resources may be limiting? (storage, communication, etc.)

A smart algorithm is a lot more important than a fast computer

Professor Martin GrΓΆtschel, Konrad-Zuse-Zentrum fΓΌr Informationstechnik Berlin, expert in optimization

β€œa benchmark production planning model solved using linear programming would have taken 82 years to solve in 1988, using the computers and the linear programming algorithms of the day. Fifteen years later β€” in 2003 β€” this same model could be solved in roughly 1 minute, an improvement by a factor of roughly 43 million. Of this, a factor of roughly 1,000 was due to increased processor speed, whereas a factor of roughly 43,000 was due to improvements in algorithms!”

Algorithms are behind any computation done in economics:

Structural estimation of dynamic models is one of the areas of econometrics requiring quick computation β€…β€ŠβŸΉβ€…β€Š\implies smart algorithms.

Algorithms with different complexityΒΆ

Complexity of an algorithm is the cost, measured in running time or in storage requirement, of using the algorithm to solve one of the problems in the relevant class.

Let’s look at some particular algorithms.

Parity of a numberΒΆ

Check whether an integer is odd or even.

Algorithm:
Convert the number to binary
Check whether the last digit is 0 (number is even) or 1 (number is odd)
Source
n =     2 (00000010), parity=0
n =     4 (00000100), parity=0
n =     7 (00000111), parity=1
n =    32 (00100000), parity=0
n =   543 (1000011111), parity=1
n =   671 (1010011111), parity=1
n =   780 (1100001100), parity=0
Source
<Figure size 900x600 with 1 Axes>

Finding max/min of a listΒΆ

Find max or min in an unsorted list of values.

Algorithm:
cycle through the list once saving the current extremum value
Source
Source
<Figure size 900x600 with 1 Axes>

Binary search in a finite setΒΆ

Finding a discrete element between given boundaries.

Explain the operation of the code below.

Source
Inputs: sorted list of numbers, and a value to find
Algorithm:
1. Find middle point
2. If the sought value is below, reduce the list to the lower half
3. If the sought value is above, reduce the list to the upper half
Index of x[6]=[60] in [20 22 28 33 36 48 60 71 87 90] is 6
Source
<Figure size 900x600 with 1 Axes>
<Figure size 900x600 with 1 Axes>

Rate of growth and big-O notationΒΆ

A very useful way to talk about the rate of growth ↔\leftrightarrow complexity of algorithms.

In words, f(x)=O(g(x))f(x) = O\big(g(x)\big) simply means that as xx increases, f(x)f(x) certainly does not grow at a faster rate than g(x)g(x).

In measuring solution time we may distinguish performance in

Constants and lower terms are ignored because we are only interested in the order of growth.

Classes of algorithm complexityΒΆ

How many operations as function of input size?ΒΆ

Divide-and-conquer algorithmsΒΆ

Divide-and-conquer structure is what typically marks an excellent algorithm.

Curse of dimensionalityΒΆ

An example of a bad algorithm?

A practical test of whether an algorithm is subject to the curse of dimensionality is to collect the run time data for a range of input sizes nn, plot it on a semi-log scale, and see if the curve is convex or concave.

Let r(n)r(n) denote run time. The borderline between the two cases is

(ln⁑r(n))β€²β€²=0β€…β€ŠβŸΉβ€…β€Šln⁑r(n)=an+bβ€…β€ŠβŸΉβ€…β€Šr(n)=ebeanβ€…β€ŠβŸΉβ€…β€Šr(n)=O(An),(\ln r(n))'' = 0 \implies \ln r(n) = a n + b \implies r(n) = e^b e^{a n} \implies r(n) = O(A^n),

that is exactly the boundary of the curse of dimensionality.

One caveat in this practical test is that the complexity is measured at the limit as nβ†’βˆžn\to \infty, but the test considers the convexity/concavity of the curve only for limited nn. Even when the problem appears to be sub-exponential for given nn, it may still be exponential in the limit.

Theoretical analysis of algorithm complexityΒΆ

Essentially boils down to counting the number of operations as a function of the input size nn, and then analyzing the limiting behavior of that function.

It is important to properly define what is meant by an β€œoperation” β€” in principle, it depends on the computer architecture, and what a given processor can do in one clock cycle. In practice, we often assume that all basic arithmetic operations take the same time, and that the time to read/write a number from/to memory is negligible.

Second important component is to define the input size nn β€” for example, the number of elements in a list, or the number of bits in an integer.

Classes of computational complexityΒΆ

Theoretical computer science has a well-developed framework for classifying problems by their complexity. It is most useful in studying how problems convert to each other, forming the classes.

Thinking of all problems there are:

NP stands for non-deterministic polynomial time ↔\leftrightarrow β€˜magic’ guess algorithm.

P vs. NP

Unresolved question of whether P = NP or P β‰ \ne NP ($1 mln. prize by the Clay Mathematics Institute)

Complexity classes

Optimal allocation of a discrete goodΒΆ

Let’s consider the following optimization problem in economics.

A planner has a fixed quantity AA of one good and nn agents to give it to.

Utility/welfare W(x1,…,xn)W(x_1,\dots,x_n) depends on how much each agent receives.

The good comes in indivisible units of size Ξ›\Lambda β€” whole machines, hospital beds, nurses on a shift, seats on a flight β€” so an agent’s share cannot be fine-tuned, only set to a whole number of units.

The indivisibility changes the nature of the problem!

The problem:

Maximize W(x1,x2,…,xn)W(x_1,x_2,\dots,x_n) subject to βˆ‘i=1nxi=A\sum_{i=1}^{n}x_i = A, where AA is divisible only in steps of Ξ›\Lambda.

Let M=A/Ξ›βˆˆNM=A/\Lambda \in \mathbb{N} be the number of units to distribute.

Let pi∈{0,1,…,M}p_i \in \{0,1,\dots,M\} be the number units given to agent ii.

The problem becomes

Maximize W(Ξ›p1,Ξ›p2,…,Ξ›pn)W(\Lambda p_1,\Lambda p_2,\dots,\Lambda p_n) over all (p1,…,pn)(p_1,\dots,p_n) such that pi∈{0,…,M}p_i \in \{0,\dots,M\} and βˆ‘i=1npi=M\sum_{i=1}^{n}p_i = M.

So the economics has turned into a combinatorial question: enumerate every composition of MM into nn parts.

  • What is total number of compositions of MM into nn parts?

Thus, N(M,n)N(M,n) for fixed MM grows exponentially in the number of agents nn.

This is the curse of dimensionality arriving in a problem that looked entirely innocent β€” nothing about β€œsplit a good between agents” hints that adding one more agent could double the work.

The enumeration algorithmΒΆ

We need each composition exactly once, and we want them one at a time rather than all at once β€” the whole point is that the full list does not fit anywhere.

Order the compositions and step from one to the next. Start with everything in the last part, and repeatedly move one unit leftwards, flushing whatever sits to the right back into the last position:

Input: M (units of the good), n (number of agents)

Algorithm:
  start from (0, 0, ..., 0, M)      # everything to the last part
  report this composition
  while the first part is not M:    # (M, 0, ..., 0) is the last one
    step 1: find the right-most non-zero part i
    step 2: move one unit from part i to part i-1
    step 3: move everything remaining in part i to the last part
    report the current composition

Each step does a constant amount of work, so the run time is proportional to the number of compositions β€” and that is where the exponential growth comes from, not from the bookkeeping.

Here is the list of all compositions of 5 into 3 parts:

ReferencesΒΆ
  1. Wilf, H. S. (2002). Algorithms and Complexity. A K Peters/CRC Press.