# Backtracking

Backtracking is a more intelligent variation of this approach. The principal idea is to construct solutions one component at a time and evaluate such partially constructed candidates as follows.

Backtracking

Throughout the book (see in particular Sections 3.4 and 11.3), we have encoun-tered problems that require finding an element with a special property in a domain that grows exponentially fast (or faster) with the size of the problem’s input: a Hamiltonian circuit among all permutations of a graph’s vertices, the most valu-able subset of items for an instance of the knapsack problem, and the like. We addressed in Section 11.3 the reasons for believing that many such problems might not be solvable in polynomial time. Also recall that we discussed in Section 3.4 how such problems can be solved, at least in principle, by exhaustive search. The exhaustive-search technique suggests generating all candidate solutions and then identifying the one (or the ones) with a desired property.

Backtracking is a more intelligent variation of this approach. The principal idea is to construct solutions one component at a time and evaluate such partially constructed candidates as follows. If a partially constructed solution can be de-veloped further without violating the problem’s constraints, it is done by taking the first remaining legitimate option for the next component. If there is no legiti-mate option for the next component, no alternatives for any remaining component need to be considered. In this case, the algorithm backtracks to replace the last component of the partially constructed solution with its next option.

It is convenient to implement this kind of processing by constructing a tree of choices being made, called the state-space tree. Its root represents an initial state before the search for a solution begins. The nodes of the first level in the tree represent the choices made for the first component of a solution, the nodes of the second level represent the choices for the second component, and so on. A node in a state-space tree is said to be promising if it corresponds to a partially constructed solution that may still lead to a complete solution; otherwise,

it is called nonpromising. Leaves represent either nonpromising dead ends or complete solutions found by the algorithm. In the majority of cases, a state-space tree for a backtracking algorithm is constructed in the manner of depth-first search. If the current node is promising, its child is generated by adding the first remaining legitimate option for the next component of a solution, and the processing moves to this child. If the current node turns out to be nonpromising, the algorithm backtracks to the node’s parent to consider the next possible option for its last component; if there is no such option, it backtracks one more level up the tree, and so on. Finally, if the algorithm reaches a complete solution to the problem, it either stops (if just one solution is required) or continues searching for other possible solutions.

n-Queens Problem

As our first example, we use a perennial favorite of textbook writers: the n-queens problem. The problem is to place n queens on an n × n chessboard so that no two queens attack each other by being in the same row or in the same column or on the same diagonal. For n = 1, the problem has a trivial solution, and it is easy to see that there is no solution for n = 2 and n = 3. So let us consider the four-queens problem and solve it by the backtracking technique. Since each of the four queens has to be placed in its own row, all we need to do is to assign a column for each queen on the board presented in Figure 12.1.

We start with the empty board and then place queen 1 in the first possible position of its row, which is in column 1 of row 1. Then we place queen 2, after trying unsuccessfully columns 1 and 2, in the first acceptable position for it, which is square (2, 3), the square in row 2 and column 3. This proves to be a dead end because there is no acceptable position for queen 3. So, the algorithm backtracks and puts queen 2 in the next possible position at (2, 4). Then queen 3 is placed at (3, 2), which proves to be another dead end. The algorithm then backtracks all the way to queen 1 and moves it to (1, 2). Queen 2 then goes to (2, 4), queen 3 to (3, 1), and queen 4 to (4, 3), which is a solution to the problem. The state-space tree of this search is shown in Figure 12.2.

If other solutions need to be found (how many of them are there for the four-queens problem?), the algorithm can simply resume its operations at the leaf at which it stopped. Alternatively, we can use the board’s symmetry for this purpose.  Finally, it should be pointed out that a single solution to the n-queens problem for any n 4 can be found in linear time. In fact, over the last 150 years mathe-maticians have discovered several alternative formulas for nonattacking positions of n queens [Bel09]. Such positions can also be found by applying some general algorithm design strategies (Problem 4 in this section’s exercises).

Hamiltonian Circuit Problem

As our next example, let us consider the problem of finding a Hamiltonian circuit in the graph in Figure 12.3a.

Without loss of generality, we can assume that if a Hamiltonian circuit exists, it starts at vertex a. Accordingly, we make vertex a the root of the state-space tree (Figure 12.3b). The first component of our future solution, if it exists, is a first intermediate vertex of a Hamiltonian circuit to be constructed. Using the alphabet order to break the three-way tie among the vertices adjacent to a, we select vertex b. From b, the algorithm proceeds to c, then to d, then to e, and finally to f, which proves to be a dead end. So the algorithm backtracks from f to e, then to d, and then to c, which provides the first alternative for the algorithm to pursue. Going from c to e eventually proves useless, and the algorithm has to backtrack from e to c and then to b. From there, it goes to the vertices f , e, c, and d, from which it can legitimately return to a, yielding the Hamiltonian circuit a, b, f , e, c, d, a. If we wanted to find another Hamiltonian circuit, we could continue this process by backtracking from the leaf of the solution found.

Subset-Sum Problem

As our last example, we consider the subset-sum problem: find a subset of a given set A = {a1, . . . , an} of n positive integers whose sum is equal to a given positive integer d. For example, for A = {1, 2, 5, 6, 8} and d = 9, there are two solutions: {1, 2, 6} and {1, 8}. Of course, some instances of this problem may have no solutions.

It is convenient to sort the set’s elements in increasing order. So, we will assume that

a1 < a2 < . . . < an. The state-space tree can be constructed as a binary tree like that in Figure 12.4 for the instance A = {3, 5, 6, 7} and d = 15. The root of the tree represents the starting point, with no decisions about the given elements made as yet. Its left and right children represent, respectively, inclusion and exclusion of a1 in a set being sought. Similarly, going to the left from a node of the first level corresponds to inclusion of a2 while going to the right corresponds to its exclusion, and so on. Thus, a path from the root to a node on the ith level of the tree indicates which of the first i numbers have been included in the subsets represented by that node.

We record the value of s, the sum of these numbers, in the node. If s is equal to d, we have a solution to the problem. We can either report this result and stop or, if all the solutions need to be found, continue by backtracking to the node’s parent. If s is not equal to d, we can terminate the node as nonpromising if either of the following two inequalities holds: General Remarks

From a more general perspective, most backtracking algorithms fit the follow-ing description. An output of a backtracking algorithm can be thought of as an n-tuple (x1, x2, . . . , xn) where each coordinate xi is an element of some finite linearly ordered set Si. For example, for the n-queens problem, each Si is the set of integers (column numbers) 1 through n. The tuple may need to satisfy some additional constraints (e.g., the nonattacking requirements in the n-queens prob-lem). Depending on the problem, all solution tuples can be of the same length (the n-queens and the Hamiltonian circuit problem) and of different lengths (the subset-sum problem). A backtracking algorithm generates, explicitly or implic-itly, a state-space tree; its nodes represent partially constructed tuples with the first i coordinates defined by the earlier actions of the algorithm. If such a tuple (x1, x2, . . . , xi) is not a solution, the algorithm finds the next element in Si+1 that is consistent with the values of (x1, x2, . . . , xi) and the problem’s constraints, and adds it to the tuple as its (i + 1)st coordinate. If such an element does not exist, the algorithm backtracks to consider the next value of xi, and so on.

To start a backtracking algorithm, the following pseudocode can be called for i = 0 ; X[1..0] represents the empty tuple.

ALGORITHM     Backtrack(X[1..i])

//Gives a template of a generic backtracking algorithm

//Input: X[1..i] specifies first i promising components of a solution //Output: All the tuples representing the problem’s solutions

if X[1..i] is a solution write X[1..i]

else        //see Problem 9 in this section’s exercises

for each element x Si+1 consistent with X[1..i] and the constraints do

X[i + 1] x

Backtrack(X[1..i + 1])

Our success in solving small instances of three difficult problems earlier in this section should not lead you to the false conclusion that backtracking is a very efficient technique. In the worst case, it may have to generate all possible candidates in an exponentially (or faster) growing state space of the problem at hand. The hope, of course, is that a backtracking algorithm will be able to prune enough branches of its state-space tree before running out of time or memory or both. The success of this strategy is known to vary widely, not only from problem to problem but also from one instance to another of the same problem.

There are several tricks that might help reduce the size of a state-space tree. One is to exploit the symmetry often present in combinatorial problems. For example, the board of the n-queens problem has several symmetries so that some solutions can be obtained from others by reflection or rotation. This implies, in particular, that we need not consider placements of the first queen in the last n/2 columns, because any solution with the first queen in square (1, i), n/2 i n, can be obtained by reflection (which?) from a solution with the first queen in square (1, n i + 1). This observation cuts the size of the tree by about half. Another trick is to preassign values to one or more components of a solution, as we did in the Hamiltonian circuit example. Data presorting in the subset-sum example demonstrates potential benefits of yet another opportunity: rearrange data of an instance given.

It would be highly desirable to be able to estimate the size of the state-space tree of a backtracking algorithm. As a rule, this is too difficult to do analytically, however. Knuth [Knu75] suggested generating a random path from the root to a leaf and using the information about the number of choices available during the path generation for estimating the size of the tree. Specifically, let c1 be the number of values of the first component x1 that are consistent with the problem’s constraints. We randomly select one of these values (with equal probability 1/c1) to move to one of the root’s c1 children. Repeating this operation for c2 possible values for x2 that are consistent with x1 and the other constraints, we move to one of the c2 children of that node. We continue this process until a leaf is reached after randomly selecting values for x1, x2, . . . , xn. By assuming that the nodes on level i have ci children on average, we estimate the number of nodes in the tree as

1 + c1 + c1c2 + . . . + c1c2 . . . cn.

Generating several such estimates and computing their average yields a useful estimation of the actual size of the tree, although the standard deviation of this random variable can be large.

In conclusion, three things on behalf of backtracking need to be said. First, it is typically applied to difficult combinatorial problems for which no efficient algo-rithms for finding exact solutions possibly exist. Second, unlike the exhaustive-search approach, which is doomed to be extremely slow for all instances of a problem, backtracking at least holds a hope for solving some instances of nontriv-ial sizes in an acceptable amount of time. This is especially true for optimization problems, for which the idea of backtracking can be further enhanced by evaluat-ing the quality of partially constructed solutions. How this can be done is explained in the next section. Third, even if backtracking does not eliminate any elements of a problem’s state space and ends up generating all its elements, it provides a specific technique for doing so, which can be of value in its own right.

Exercises 12.1

a. Continue the backtracking search for a solution to the four-queens prob-lem, which was started in this section, to find the second solution to the problem.

Explain how the board’s symmetry can be used to find the second solution to the four-queens problem.

a. Which is the last solution to the five-queens problem found by the back-tracking algorithm?

Use the board’s symmetry to find at least four other solutions to the problem.

a. Implement the backtracking algorithm for the n-queens problem in the lan-guage of your choice. Run your program for a sample of n values to get the numbers of nodes in the algorithm’s state-space trees. Compare these num-bers with the numbers of candidate solutions generated by the exhaustive-search algorithm for this problem (see Problem 9 in Exercises 3.4).

For each value of n for which you run your program in part (a), estimate the size of the state-space tree by the method described in Section 12.1 and compare the estimate with the actual number of nodes you obtained.

Design a linear-time algorithm that finds a solution to the n-queens problem for any n 4.

Apply backtracking to the problem of finding a Hamiltonian circuit in the following graph. Apply backtracking to solve the 3-coloring problem for the graph in Fig-ure 12.3a.

Generate all permutations of {1, 2, 3, 4} by backtracking.

a. Apply backtracking to solve the following instance of the subset sum problem: A = {1, 3, 4, 5} and d = 11.

Will the backtracking algorithm work correctly if we use just one of the two inequalities to terminate a node as nonpromising?

The general template for backtracking algorithms, which is given in the sec-tion, works correctly only if no solution is a prefix to another solution to the problem. Change the template’s pseudocode to work correctly without this restriction.

Write a program implementing a backtracking algorithm for

the Hamiltonian circuit problem.

the m-coloring problem.

Puzzle pegs This puzzle-like game is played on a board with 15 small holes arranged in an equilateral triangle. In an initial position, all but one of the holes are occupied by pegs, as in the example shown below. A legal move is a jump of a peg over its immediate neighbor into an empty square opposite; the jump removes the jumped-over neighbor from the board. Design and implement a backtracking algorithm for solving the following versions of this puzzle.

Starting with a given location of the empty hole, find a shortest sequence of moves that eliminates 14 pegs with no limitations on the final position of the remaining peg.

Starting with a given location of the empty hole, find a shortest sequence of moves that eliminates 14 pegs with the remaining peg at the empty hole of the initial board.

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail
Introduction to the Design and Analysis of Algorithms : Coping with the Limitations of Algorithm Power : Backtracking |

Related Topics