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: Complexity and Heuristics

Open In Colab

In the previous notebooks we solved the shortest path and maximum flow problems with fast exact algorithms, while for the traveling salesman problem we only found an exact algorithm that tries all tours, which takes far too long for large instances. Complexity theory is about the running times of algorithms, and it explains this difference. It also tells us what to expect in practice: whether a solver will find the optimal solution of a large instance, or whether a heuristic is the better choice. In the second part of this notebook we continue with the TSP example and solve it with a heuristic, the 2-opt heuristic.

Learning outcomes

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

  • explain the difference between polynomial and non-polynomial running-time growth, and why it matters in practice;

  • classify the problems of this course as belonging to P or being NP-complete, and explain the practical consequence of that classification;

  • explain why LO is efficiently solvable in practice although the simplex method is not polynomial;

  • distinguish metaheuristics from problem-specific heuristics;

  • apply the 2-opt heuristic by hand, and explain the difference between a local and a global optimum.

Running Time

The running time of an algorithm depends on the size of the instance, for example the number of nodes nn of a network. We count the number of basic operations the algorithm performs as a function of nn, and are only interested in how fast it grows. Some examples from this lecture:

  • Dijkstra’s algorithm repeats its loop nn times and looks at no more than nn nodes per repetition, so it takes about n2n^2 operations;

  • trying all tours of a TSP means checking (n−1)!(n - 1)! tours, which takes about n!n! operations.

A precise count, for example c⋅n(n−1)/2c \cdot n(n - 1)/2 operations with cc the number of operations to update one node, does not matter much: for large nn, the term n2n^2 determines the growth. We write that the running time is O(n2)O(n^2), “of the order n2n^2”. An algorithm is polynomial if its running time is O(nk)O(n^k) for some fixed power kk, and non-polynomial otherwise, for example if it grows like 2n2^n or n!n!.

The difference is huge, as Table 1 shows.

Table 1:Values of polynomial and non-polynomial functions.

nnlog⁡2n\log_2 nnlog⁡2nn \log_2 nn2n^2n5n^52n2^nn!n!
1001121
52.3211.625312532120
103.3233.210010510243.6⋅1063.6 \cdot 10^6
204.3286.44003.2⋅1063.2 \cdot 10^61.0⋅1061.0 \cdot 10^62.4⋅10182.4 \cdot 10^{18}
505.6428225003.1⋅1083.1 \cdot 10^81.1⋅10151.1 \cdot 10^{15}3.0⋅10643.0 \cdot 10^{64}
1006.6466410410101.3⋅10301.3 \cdot 10^{30}9.3⋅101579.3 \cdot 10^{157}
10009.97996610610151.1⋅103011.1 \cdot 10^{301}4.0⋅1025674.0 \cdot 10^{2567}

Suppose a computer performs a billion (109) operations per second. An algorithm with n5n^5 operations then solves an instance with n=100n = 100 in 10 seconds. An algorithm with 2n2^n operations takes about 4⋅10134 \cdot 10^{13} years for the same instance, thousands of times the age of the universe, and one with n!n! operations takes even longer: there are only about 1080 atoms in the universe. Faster computers do not help much. If computer power doubles every two years, as Moore’s law roughly says, a 2n2^n algorithm can handle one extra node every two years. A quantum computer could at best take the square root of these numbers for some problems, which still leaves 100!≈3⋅1078\sqrt{100!} \approx 3 \cdot 10^{78}.

P and NP-Complete

Researchers try to find polynomial algorithms for every problem. For some problems they succeeded, and proved that the algorithm always finds an optimal solution. For other problems, such as the TSP, hundreds of researchers have looked for a polynomial algorithm for decades without success. Based on the running time of the best known exact algorithm, there are, roughly speaking, two classes of problems.

  • P is the class of problems for which a polynomial exact algorithm is known. Optimal solutions can be found even for large instances. Examples are the transportation problem, the shortest path problem, the maximum flow problem, the product-mix problem and LO in general.

  • NP-complete problems are problems for which no polynomial exact algorithm is known. Large instances are solved with heuristics, or with solvers that may need a very long time to prove optimality. Examples are the knapsack problem, machine scheduling, set covering, the TSP and ILO in general.

The NP-complete problems are, in a precise sense, all equally hard: each of them can be translated into any other in polynomial time. So a polynomial algorithm for one NP-complete problem would give a polynomial algorithm for all of them.

For the modeling in this course, this means that adding integer variables to an LO model can change a problem that solves in a second into one that a solver cannot finish.

How to Win a Million Dollars

Whether NP-complete problems can be solved in polynomial time is an open question, known as “P versus NP”. It is one of the seven Millennium Prize Problems, for each of which the Clay Mathematics Institute offers US$1 million. To win, do one of the following:

  1. find a polynomial exact algorithm for an NP-complete problem, or

  2. prove that no polynomial exact algorithm exists for an NP-complete problem.

Most researchers believe that the second statement is true, but nobody has proved it. Of the seven Millennium Prize Problems, only the Poincaré conjecture has been officially solved so far, by Grigori Perelman in 2003. In September 2026, OpenAI claimed that its AI system had solved another one, the Navier-Stokes problem about the equations that describe how fluids flow (Nature, 2026). At the time of writing, mathematicians are still checking the proof.

Heuristics

For NP-complete problems such as the TSP, no polynomial exact algorithm is known. A heuristic does not aim for the optimum but for a near-optimal solution in a reasonable amount of time. There are two types:

  • Metaheuristics are not specific to one problem. Examples are local search, evolutionary algorithms, tabu search and simulated annealing. They still need some tailoring to the problem at hand, for example a definition of which solutions are “close” to each other.

  • Problem-specific heuristics use the structure of one problem. For common problems there is a lot of literature on them. The 2-opt heuristic for the TSP, below, is an example.

Before using a heuristic, ask whether you need the optimal solution at all, and how much worse than optimal a solution may be. A heuristic usually gives no guarantee about how far its solution is from the optimum, so it can be hard to tell whether a solution is good enough. Comparing heuristics on the same instances, or with a lower bound such as the value of an LO relaxation, helps.

The 2-Opt Heuristic

The 2-opt heuristic is a local search method. Local search starts with some solution and repeatedly moves to a better solution close to the current one, until there is no better solution close by. “Close” is made precise by a neighborhood: for every solution TT, a set N(T)N(T) of solutions that can be reached from TT with one small change. Writing L(T)L(T) for the length of tour TT, local search is:

  • Start: an initial tour TT.

  • While N(T)N(T) contains a tour T′T' with L(T′)<L(T)L(T') < L(T):

    • T=T′T = T'.

The final tour has no better tour in its neighborhood: it is a local optimum. There is no guarantee that it is a global optimum, a best tour overall.

The 2-opt heuristic uses the TSP-specific neighborhood N(T)N(T) of all tours that can be reached from TT with one 2-opt swap:

  1. remove two edges of the tour that do not share a node;

  2. reconnect the two pieces into a tour, with the other two edges that do so.

There is only one other way to reconnect the pieces into one tour: if the tour goes ⋯→a→b→⋯→c→d→…\dots \to a \to b \to \dots \to c \to d \to \dots and we remove the edges aa-bb and cc-dd, the new tour goes ⋯→a→c→⋯→b→d→…\dots \to a \to c \to \dots \to b \to d \to \dots, where the part from bb to cc is now traveled in the opposite direction. The length changes by

dac+dbd−dab−dcd,d_{ac} + d_{bd} - d_{ab} - d_{cd},

so the swap is an improvement if this is negative. For the initial tour, a simple choice is to start at some node and repeatedly move to a random unvisited node.

Example

We continue with the example of Traveling Salesman Problem, with the same distances and the same function tour_length as there. A missing edge has distance ∞\infty.

Source
<Figure size 900x480 with 1 Axes>

Figure 1:The TSP example network with the initial tour A → B → D → F → E → C → A of length 19 (red).

We start with the tour A → B → D → F → E → C → A of length 19 in Figure 1. Consider the swap that removes the edges B-D (length 3) and E-C (length 6). Reconnecting gives the edges B-E (length 2) and D-C (length 3), and the length changes by 2+3−3−6=−42 + 3 - 3 - 6 = -4. The new tour A → B → E → F → D → C → A in Figure 2 has length 15: the part D → F → E is now traveled as E → F → D. No 2-opt swap improves this tour, so it is a local optimum. Here it is also a global optimum: in Traveling Salesman Problem, pulp and brute force found the same length.

<Figure size 900x480 with 1 Axes>

Figure 2:The tour A → B → E → F → D → C → A of length 15 (red), found with one 2-opt swap from the tour in Figure 1.

2-Opt in Python

The function better_neighbor goes through all 2-opt swaps of a tour and returns the first tour in its neighborhood that is shorter, or None if there is none. A swap that removes the edges after positions i and j reverses the part of the tour in between. The loop at the bottom is the local search algorithm above.

initial tour: ABDFEC length 19
better tour:  ABEFDC length 15
local optimum: ABEFDC

Local and Global Optima

How good the local optimum is depends on the initial tour and on the neighborhood. Starting from another initial tour can end in another local optimum, so a common approach is to run the heuristic from several initial tours and keep the best result. A larger neighborhood gives better local optima, but takes longer to search. The 3-opt heuristic removes three edges and tries all ways of reconnecting the pieces, and is known to give better tours than 2-opt. In general, the kk-opt neighborhood removes kk edges and contains in the order of nkn^k tours.

<Figure size 900x480 with 1 Axes>

Figure 3:Undirected graph of Exercise 1, with distances along the edges.

References

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