New Doc1
New Doc1
GROUP 4 MEMBER
NAME ID
1. Excessive recursion..................................................................................................................................2
2. Backtracking............................................................................................................................................9
1 N-Queens Problem..................................................................................................................11
2 Subset Sum..............................................................................................................................16
Reference...................................................................................................................................................18
I
List of figure
Content page
Figure 2 Number of addition operation and number of recursive calls to calculate Fibonacci
numbers .............................................................................5
II
Introduction
1
1. Excessive recursion
Recursion is a technique where a function calls itself to solve a problem by breaking it down into
smaller, self-similar sub-problems. Excessive recursion occurs when a recursive function calls
itself repeatedly without a proper base case or stopping condition, leading to a stack overflow
error.
Logical simplicity and readability are used as an argument supporting the use of recursion. The
price for using recursion is slowing down execution time and storing on the run-time stack more
things than required in a non recursive approach. If recursion is too deep (for example,
computing 5.6100,000), then we can run out of space on the stack and our program crashes. But
usually, the number of recursive calls is much smaller than 100,000, so the danger of
overflowing the stack may not be imminent.However, if some recursive function repeats the
computations for some parameters, the run time can be prohibitively long even for very simple
cases.A recursive method is excessively recursive if it repeats computations for some parameter
values.
2
A The fibonacci numbers
Fib(n) = { n if n<2
Fib(n-2) + Fib(n-1) otherwise
The definition states that if the first two numbers are 0 and 1, then any number in the sequence is
the sum of its two predecessors. But these predecessors are in turn sums of their predecessors,
and so on, to the beginning of the sequence. The sequence produced by the definition is
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,...
How can this definition be implemented in C++? It takes almost term-by-term translation to have
a recursive version, which is:
unsigned int Fib (unsigned int n)
{
if (n < 2)
return n;
// else
return Fib(n-2) Fib(n-1);
}
The function is simple and easy to understand but extremely inefficient. To see it, compute Fib
(6), the seventh number of the sequence, which is 8. Based on the definition, the computation
runs as follows:
Even if we try to compute the value of 5.6100,000 using an iterative algorithm, we are not
completely free from a troublesome situation since the number is much too large to fit even a
variable of double length. Thus, although the program would not crash, the computed value
would be incorrect (why?), which may be even more dangerous than a program crash.
3
Fib(6)= fib(4) +fib(5)
= fib(2) + fib(3) +fib(5)
= fib(0) + fib(1) + fib(3) +fib(5)
= 0 + 1 + fib(3) +fib(5)
= 1 +fib(1) + fib(2) +fib(5)
= 1 +fib(1)+fib(0)+fib(1) +fib(5)
etc.
This is just the beginning of our calculation process, and even here there are certain shortcuts.
All these calculations can be expressed more concisely in the form of the tree shown in Figure 1.
Tremendous inefficiency results because Fib() is called 25 times to determine the seventh
element of the Fibonacci sequence. The source of this inefficiency is the repetition of the same
calculations because the system forgets what has already been calculated. For example, Fib() is
called eight times with parameter n = 1 to decide that I can be returned. For each number of the
sequence, the function computes all its predecessors without taking into account that it suffices
to do this only once. To find Fib(6) = 8, it computes Fib (5), Fib(4), Fib(3), Fib(2), Fib(1), and
Fib(0) first. To determine these values, Fib(4),..., Fib (0) have to be computed to know the value
of Fib (5). Independently of this, the chain of computations Fib(3)..... Fib(0) is executed to find
Fib(4).
We can prove that the number of additions required to find Fib(n) using a re-cursive definition is
equal to Fib(n + 1)-1. Counting two calls per one addition plus the very first call means that Fib()
is called * Fib(n + 1) - 1 times to compute Fib(n). This number can be exceedingly large for
fairly small us, as the table in Figure 2. indicates.
It takes almost a quarter of a million calls to find the twenty-sixth Fibonacci number, and nearly
3 million calls to determine the thirty-first! This is too heavy a price for the simplicity of the
recursive algorithm. As the number of calls and the run time grow exponentially with n. the
algorithm has to be abandoned except for very small numbers.
4
Figure 1 the tree of calls for fib(6)
Figure 2 Number of addition operation and number of recursive calls to calculate Fibonacci
numbers
5
Implementation of Fibonacci
Tower of Hanoi is a mathematical puzzle where we have three rods (A, B, and C) and N disks.
Initially, all the disks are stacked in decreasing value of diameter i.e., the smallest disk is placed
on the top and they are on rod A. The objective of the puzzle is to move the entire stack to
another rod (here considered C), obeying the following simple rules:
Only one disk can be moved at a time.
Each move consists of taking the upper disk from one of the stacks and placing it on top of
another stack i.e. a disk can only be moved if it is the uppermost disk on a stack.
No disk may be placed on top of a smaller disk.
The idea is to use the helper node to reach the destination using recursion. Below is the pattern
for this problem:
6
Shift (N-1) disks from ‘A’ to ‘B’, using C.
Shift last disk from ‘A’ to ‘C’.
Shift( N-1) disks from ‘B’ to ‘C’, using A.
faq.disk3
Image illustration for 3 disks.
7
The implementation of tower of Hanoi.
#include <bits/stdc++.h>
using namespace std;
void towerOfHanoi(int n, char from_rod, char to_rod,
char aux_rod)
{
if (n == 0) {
return;
}
towerOfHanoi(n - 1, from_rod, aux_rod, to_rod);
cout << "Move disk " << n << " from rod " << from_rod
<< " to rod " << to_rod << endl;
towerOfHanoi(n - 1, aux_rod, to_rod, from_rod);
}
// Driver code
int main()
{
int N = 3;
8
2. Backtracking
The choice between using backtracking or recursion depends on the problem's nature and
requirements, and skilled practitioners often combine these techniques to develop efficient and
elegant solutions to challenging computational problems.
9
2.1. Introduction to Backtracking
1 N-Queens Problem: Placing N chess queens on an N×N chessboard so that no two queens
threaten each other. Sudoku: Filling a 9×9 grid with digits so that each column, each row, and
each of the nine 3×3 subgrids contain all of the digits from 1 to 9.
2 Subset Sum: Finding a subset of numbers from a given set that adds up to a specific target
sum. Backtracking is a powerful technique for solving complex, combinatorial problems, but it's
essential to design the algorithm carefully to avoid inefficient exploration of the search space.
Proper pruning techniques, such as constraint propagation and heuristics, can significantly
improve the efficiency of backtracking algorithms.
3 Combinatorial Optimization: Problems like the Traveling Salesman Problem (TSP), where
you find the shortest possible route that visits a given set of cities and returns to the origin city.
10
In summary, backtracking is a systematic approach to problem-solving that explores a decision
tree, making choices along the way and backtracking when necessary to find a valid solution or
determine that no solution exists. It's a versatile technique used in various fields, including
artificial intelligence, optimization, and puzzle-solving.
1 N-Queens Problem
The eight queens problem is based on chess. A chess board is an eight by eight grid of squares. A
queen is a piece that can attack another piece if and only if that piece lies in the same row or
column as the queen, or along either of the two diagonals through the queen's square. The Eight
Queens Problem asks for a set of eight squares on which to place eight queens in such a way that
none can attack any other.
There is a natural backtracking solution to this problem. Since there must be exactly one queen
in each column of the board, and exactly one queen in each row, every queen must be in its own
unique row and column. We arbitrarily use the columns of the board to organize the search.
Assume the columns are numbered 1 to 8. Try to think recursively now. Imagine that you have
placed queens on the board already and that the queens that have been placed so far cannot attack
each other. In other words, so far the queens on the board are a potential solution. Initially this is
true because no queens are on the board. You have been placing the queens on the board by
putting them in successive columns. Now suppose further that you have a means of checking, for
any given potential position in which you want to place the current queen, whether doing so
would put it under attack by one of the queens already on the board. The task at the moment is to
place the queen in the current column, so you try each row position in that column to see if it
leads to a solution. If there is a row that is safe in this column, then you place the queen and
recursively advance to the next column, trying to place the next queen there, unless of course you
just placed the 8th queen, in which case you found a solution. However, if there is no row in the
current column that is safe for the queen, then you have to backtrack you have to go back to the
11
queen you placed in the preceding column and try a different row for it, repeatedly if necessary,
until it is safe, applying this same recursive strategy there. If you backtrack to the very first
queen and cannot find a row for it, then there is no solution to this problem.
A classic combinatorial problem is to place 8 queens on a 8*8 chess board so that no two
attack, i.,e no two queens are to the same row, column or diagonal.
Now, we will solve 8 queens problem by using similar procedure adapted for 4 queens problem.
The algorithm of 8 queens problem can be obtained by placing n=8, in N queens algorithm.
If two queens are placed at positions (i,j) and (k,l). They are on the same diagonal only if
i-j=k-l ……………….(1)or
i+j=k+l .....................(2).
The solution of 8 queens problem can be obtained similar to the solution of 4 queens.
The eight queens problem attempts to place eight queens on a chessboard in such a way that no
queen is attacking any other. The rules of chess say that a queen can take another piece if it lies
on the same row, on the same column, or on the same diagonal as the queen (see Figure 4). To
solve this problem, we try to put the first queen on the board, then the second so that it cannot
take the first, then the third so that it is not in conflict with the two already placed, and so on,
until all of the queens are placed.What happens if, for instance, the sixth queen cannot be placed
in a non-conflicting position?
We choose another position for the fifth queen and try again with the sixth. If this does not work
the fifth queen is moved again. If all the possible positions for the fifth queen have been tried, the
fourth queen is moved and then the process restarts. This process requires a great deal of effort,
most of which is spent backtracking to the first crossroads offering some untried avenues. In
12
terms of code, however, the process is rather simple due to the power of recursion which is a
natural implementation of backtracking. Pseudo code for this backtracking algorithm is as
follows (the last line pertains to backtracking):
class ChessBoard {
public:
ChessBoard();//8 x 8 chessboard;
void findSolutions();
void putQueen(int);
void initializeBoard();
};
13
ChessBoard:: ChessBoard() available(true), squares(8), norm(squares-1)
initializeBoard();
norm(squares-1) {
initializeBoard();
register int i;
positionInRow[i] = -1;
column[i] = available;
howMany = 0;
14
leftDiagonal [row+col] == available &&
rightDiagonal[row-col+norm] == available) {
positionInRow[row] = col;
column[col] =!available;
leftDiagonal[row+col] = !available;
if (row squares-1)
putQueen (row+1);
else printBoard(cout);
column[col] = available;
rightDiagonal[row-col+norm]= available;
putQueen(0);
15
2 Subset Sum
La Grange's 4-squares theorem states that any natural number can be written as the sum of four
squares (including 0 if need be.) Suppose we wanted an algorithm that, given any natural
number, could find four squares whose sum equals the number. Backtracking can do this. The
idea is to pick a square less than the number, subtract it, and then find three numbers that add up
to the difference. If we succeed, then we found the four numbers. If we do not, then we back up
to the first number we picked and try a different first. number. We can proceed by picking the
smallest first number first, or by picking the largest first number first. Either way we can proceed
methodically. The algorithm below picks the smallest number first.
Steps:
3. If the subset is having sum M, then stop with that subset as solution.
4. If the subset is not feasible or if we have reached the end of the set, then backtrack through the
6. If we have visited all the elements without finding a suitable subset and if no backtracking is
16
Example: S = {3,5,6,7} and d = 15, Find the sum of subsets by using backtracking
17
Reference
A. Drozdek, Data Structures and Algorithms in C++ (2nd ed.). Cengage Learning, 2013. Boston,
MA, USA.page(189-199).
Carmen, T.H., Leiserson, C.E., Rivest, R.L., & Stein, C. (2009). Introduction to Algorithms (3rd
ed. ed.). MIT Press.
Sedgewick, R., & Wayne, K. (2011). Algorithms (4th ed.). Addison-Wesley Professional.
Tanenbaum A. S. Augenstein M. J. (1986). Data Structures Using C. Upper Saddle River, New
Jersey: Prentice Hall.
18