Backtracking Algorithm

Eugeniu Cozac
6 min readMar 23, 2022

--

If you’ve studied graph theory, cybersecurity, compilers, complex algorithms, or artificial intelligence, you’ve almost certainly come across or heard the word “backtracking,” which is a method for noting some or all of the solutions to several computing problems, mainly constraint satisfaction problems. It is used for the problems that have the idea of a “partial candidate solution” and allows for a rapid test to check if the candidate solution can be a complete solution. Backtracking is a popular method for resolving constraint satisfaction and puzzles.

It is mostly used in logic programming languages like Prolog. Backtracking is faster than brute force in all cases since it excludes a large pool of candidates with a single test. Backtracking is a common problem-solving method that relies heavily on recursion. A backtracking algorithm attempts to progressively create a solution to a computer problem. The tracking method finds the solution by scanning the solution space for the given problem in a systematic manner.

A Recursive Approach

Backtracking is a recursive strategy that improves the brute force method. The brute force method examines all potential candidates, but the backtracking method narrows the search by discarding candidates who do not meet specified criteria. As a result, backtracking algorithms are frequently faster than brute force methods. The term backtracking implies that if the present approach isn’t working, retreat and try something else. As a result, with this method, recursion is used.

How Does a Backtracking Algorithm Work?

  • Backtracking is a strategy of exploring new sequences of a decision until you discover what really works.
  • To solve the specific problem, backtracking explores the solution space, which is a collection of all possible solutions.
  • With each option, a new set of partial solutions emerges. DFS (Depth First Search) is used to investigate partial solutions.
  • If a partial solution meets a given bounding function, then it is investigated in depth-first order.
  • The partial solution will not be investigated further if it does not satisfy the constraint.
  • From that moment, the algorithm goes backward and investigates the next viable candidate.
  • A state-space tree is a convenient way to express such processing.

State-Space Tree

A space state tree is the one in which all of the problem’s probable choices (solution or non-solution) from the root, which represents the starting state, to the leaf, which represents the final state are mentioned. In the state-space tree, if a node provides a partially made solution that could lead to a complete solution, it is considered promising. Non-promising nodes break constraints and are therefore eliminated from further calculation. A leaf node can be either non-promising or a whole solution.

Let’s have a look at an example.

To clarify the logic underlying a backtracking process, we’ll use a very simple example. We want the three letters x, y, and z to be arranged in such a way that z cannot be next to x. We’ll start by creating a state-space tree, as per the approach. We’ll search for all feasible solutions and match them to the constraint and only those solutions shall be kept that meet the following criteria:

These are some likely solutions to the problems: (x,y,z), (x,z,y), (y,x,z), (y,z,x), (z,x,y), (z,y,x).

However, viable solutions to this issue are those that meet the condition that only (x,y,z) and (z,y,x) are kept in the final solution set.

Putting Backtracking into Practice

Backtracking requires us to go back after reaching a specific point or condition, and to do so, we must maintain track of what we’ve done in prior steps. As a result, a stack that follows the LIFO (Last In First Out) structure aids in achieving the goal. We employ recursion to implement a stack. Because when a recursive function reaches the base case, it follows back the original recursive calls. Simultaneously, while backtracking, we deliberately change the recursive function such that it advances with a different choice if one is available.

Steps in Backtracking Algorithm

Here is a basic implementation method that may be used to quickly develop and code a backtrack solution for most combinatorial algorithm problems. It is to be noted that the backtracking algorithm always starts on top of a partial solution, attempting to extend it to a full solution, except when there is no partial solution at the commencing our search.

Step# 01: Confirm whether the partial solution we have so far is truly a complete solution. The solution is subsequently processed, which in most situations involves displaying the values or returning a count number.

Just go to STEP 2 if you haven’t received a complete solution yet.

Step# 02: Construct a list of candidates who seem to be appropriate and eligible to be the subsequent component in the solution set for spreading the partial solution.

Step# 03: To conduct a thorough search, we must iterate over every probable candidate, placing the candidate being fetched by the iterator as the next element in the solution set, next to each iteration, and after every placement, you should return to STEP 1 to see if we have a complete answer.

Pseudocode for Backtracking Algorithm

Let’s have a look at pseudocode to help you comprehend the above-mentioned steps.

The index parameter in the backtracking algorithm shows the index of the element in the partialSolution array that will be processed next. We verify if we get a previously acquired full solution till the (index–1)-th member of the partialSolution array, which is partialSolution[0…(index–1)], just before processing the index-th entry of the partialSolution array. Remember that partialSolution is a zero-based array here.

Thus, from the above conversation, it is evident that to solve a backtracking problem, we’ll require the methods mentioned below:

  1. isACompleteSolution method determines whether the items of the partialSolution array from index 0 to (index — 1) form a proper solution; then, compute partialSolution[0…(index — 1)] and return.
  2. processSolution analyzes an answer if we have got one.
  3. constructCandidates(partialSolution, index) generates a list of all feasible candidates for partialSolution[index], i.e. the item in the partialSolution arrangement with the index ‘index’.
  4. makeMove(partialSolution, index), is used to insert a candidate component at partialSolution[index].
  5. The backtrack(…) technique recursively calls itself useful to look through all of the valid childNodes in the backtracking tree to check if the present partialSolution can be expanded to a completeSolution.
  6. undoMakeMove(partialSolution, index) is used to reset the value of partialSolution[index].
  7. Early Termination: Take a look at how we stop projects early when necessary. This isn’t true for all situations.

If you want to create all of the available solutions, you won’t need the ‘finished’ flag. However, if all you are concerned about is displaying only one answer (the one we receive first), as in Sudoku, then a ‘finished’ flag is required for early termination to indicate that now you have a result to show and there’s no need to continue.

When to use a Backtracking Algorithm?

When we have a set of options but don’t know which one will lead to the proper solution, we use backtracking algorithms. All partial candidates that could lead to a complete solution are generated by the algorithm. Backtracking algorithms have also been reported to be successful in the solution of optimization problems. In certain instances, this algorithm can help to locate all probable answers to enumeration problems.

--

--

Eugeniu Cozac

JavaScript Developer. I am proficient in building SPA with React.js