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.

Lecture 11: Algorithms and Their Characteristics

Open In Colab

In Lectures 8 to 10 we modeled problems as (I)LO models and let a solver find the optimal solution. The solver runs an algorithm, such as the simplex method or branch and bound. This notebook explains what an algorithm is, how the algorithms in this lecture are written down, and how algorithms differ from each other. The next three notebooks each take one classical problem: Shortest Path, Maximum Flow and the Traveling Salesman Problem. Each defines the problem, gives an example, and solves it in two ways: with an (I)LO approach, a model solved with pulp, and with a dedicated algorithm. The dedicated algorithm is often the way to go in practice, because it is much faster. For the traveling salesman problem, however, no fast exact algorithm is known. The last notebook, Complexity and Heuristics, explains why some problems can be solved fast and others apparently cannot, and how heuristics find good solutions for the hard ones.

Learning outcomes

On completion of this notebook, you will be able to:

  • explain what an algorithm is and give examples;

  • read an algorithm written with a start step, a repeated step and a stopping rule, and say what each intermediate quantity means;

  • characterize an algorithm as dedicated or general, as polynomial or non-polynomial, and as exact or heuristic.

What Is an Algorithm?

An algorithm is a set of computer instructions that finds a solution for a problem. More precisely, it is a computational procedure that yields a solution for every problem in a class of problems: not for one specific instance, but for every instance of the same type. Examples we have already met or will meet in this lecture:

  • the simplex method solves LO problems;

  • the branch-and-bound method solves ILO problems (see Integer Optimization);

  • Dijkstra’s algorithm solves the shortest path problem (see Shortest Path).

How We Write Down an Algorithm

Most algorithms in this lecture improve something step by step: a distance estimate, a flow, a tour. To make clear what is going on halfway through, we first define the quantities the algorithm keeps track of and say exactly what they mean at every step. The algorithm itself then has three parts:

  • Start: give every quantity its initial value;

  • While some condition holds: the steps that are repeated, each one updating the quantities;

  • the stopping rule (the condition of the while loop fails) and what the quantities then give us.

A small example shows the style. We want the largest of nn numbers a1,a2,…,ana_1, a_2, \dots, a_n. Define, for k=1,…,nk = 1, \dots, n,

mk=the largest of the first k numbers a1,…,ak.m_k = \text{the largest of the first } k \text{ numbers } a_1, \dots, a_k.

Our aim is mnm_n. The algorithm is:

  • Start: k=1k = 1 and m1=a1m_1 = a_1.

  • While k<nk < n:

    • mk+1=max⁡{mk,ak+1}m_{k+1} = \max\{m_k, a_{k+1}\};

    • k=k+1k = k + 1.

The update step is correct because of what mkm_k means: the largest of the first k+1k + 1 numbers is either the largest of the first kk numbers, or the new number ak+1a_{k+1}. When the loop stops, k=nk = n and mnm_n is the answer. In Python:

largest number: 11

The algorithm makes n−1n - 1 comparisons, one per repetition of the loop, so its running time grows linearly with nn. Counting steps like this is the start of Complexity and Heuristics.

Each algorithm in this lecture also comes with Python code that follows its description line by line, as the code above does. You do not need to be able to program these algorithms from scratch. You should be able to read the code and follow what it does: which line carries out which step of the algorithm, and what each variable holds at every step. Running the code on a small example and comparing it with your own steps by hand is a good way to check that.

Characteristics of Algorithms

Algorithms can be characterized in three ways.

  • Dedicated or general. A dedicated algorithm solves one type of problem, such as Dijkstra’s algorithm for the shortest path problem or the Ford-Fulkerson algorithm for the maximum flow problem. A general algorithm solves a wide range of problems: the simplex method solves any LO problem, branch and bound any ILO problem, and evolutionary algorithms can be applied to almost any optimization problem. A dedicated algorithm uses the structure of its problem, which usually makes it much faster than solving an (I)LO model of the same problem with a general algorithm.

  • Polynomial or non-polynomial complexity. The complexity of an algorithm describes how its running time grows with the size of the problem, for example with the number of nodes nn of a network. If the running time grows like n2n^2 (polynomial), large instances are typically no problem; if it grows like n!n! (non-polynomial), even moderate instances take forever. See Complexity and Heuristics.

  • Exact or heuristic. An exact algorithm is guaranteed to find an optimal solution. A heuristic is a best-effort method: a shortcut algorithm whose aim is not the optimal solution but a good solution in a reasonable amount of time. Training a neural network by gradient descent is an example: it finds good network weights, but nobody claims they are the best possible ones.

Why not just use the best general algorithm for everything? The no free lunch theorem (Wolpert & Macready, 1997) says that no such best algorithm exists: averaged over all possible problems, all search and optimization algorithms perform equally well. If an algorithm does better on one class of problems, it pays for that with worse performance on another class. Good performance comes from matching the algorithm to the structure of the problem, which is what dedicated algorithms do.

The table below characterizes the algorithms of Lectures 8 to 11. The running times are explained in the notebooks of the algorithms and in Complexity and Heuristics.

Table 1:Characteristics of the algorithms in Lectures 8 to 11.

algorithmproblemdedicated or generalcomplexityexact or heuristic
simplex methodLOgeneralnon-polynomial in the worst case, fast in practiceexact
branch and boundILOgeneralnon-polynomialexact
Dijkstra’s algorithmshortest pathdedicatedpolynomial (n2n^2)exact
Ford-Fulkerson algorithmmaximum flowdedicatedf∗⋅mf^* \cdot m for maximum flow f∗f^* and mm arcs, polynomial in refined implementations, see Running Timeexact
brute force (try all tours)TSPdedicatednon-polynomial (n!n!)exact
2-optTSPdedicatedfast per improvement stepheuristic

Graphs: Notation for This Lecture

The three problems in this lecture are defined on a graph: a set of nodes VV connected by arcs. An arc from node ii to node jj is written i→ji \to j or (i,j)(i, j) and has a number cijc_{ij}: a distance in the shortest path problem, a capacity in the maximum flow problem. An undirected edge between ii and jj, which can be used in both directions, is the same as two arcs i→ji \to j and j→ij \to i with the same number. The number of nodes is n=∣V∣n = |V|.

In Python we store a graph as a dictionary keyed by arcs, the same way the transportation costs were stored in Transportation and Transshipment. For example, the graph in Figure 1 is stored as:

distance from A to B: 2
Source
<Figure size 525x330 with 1 Axes>

Figure 1:The graph stored in distance, with the distance cijc_{ij} along each arc i→ji \to j.

A pair of nodes without an arc simply has no key. In the mathematical models we then set cij=∞c_{ij} = \infty for a distance (the arc is never worth using) or cij=0c_{ij} = 0 for a capacity (nothing can flow through it).

References

  • Koole, G. (2019). An Introduction to Business Analytics. Chapter 7, “Combinatorial Optimization,” introduction.

  • Wolpert, D. H., & Macready, W. G. (1997). No free lunch theorems for optimization. IEEE Transactions on Evolutionary Computation, 1(1), 67-82.