Unit 4 Mathematical Aspects and Analysis of Algorithms - 2: Structure
Unit 4 Mathematical Aspects and Analysis of Algorithms - 2: Structure
4.1 Introduction
From the previous units, we know that algorithm analysis is very important
for the efficient working of an algorithm. We analyze an algorithm for its time
and space complexities to resolve a problem easily.
In the previous unit, we analyzed non recursive algorithms. Recursive
algorithms form a better way of solving problems. Therefore, in this unit we
mathematically analyze recursive algorithms.
In this unit we will define recursion and recursive algorithm with examples.
We will also discuss the empirical analysis of algorithms and algorithm
visualization with examples.
Objectives:
After studying this unit, you should be able to:
define ‘Recursion’ and ‘Recursive algorithms’
analyze recursion with an example of Fibonacci numbers
explain empirical analysis of algorithms
describe algorithm visualization
We can use the recursion method for problems that satisfy the following two
conditions:
Each step of recursion has to break down the problem into smaller
problems of same type.
Each step of recursion needs to reduce the problem significantly.
The two types of recursion are:
Direct recursion – This is a form of recursion in which a procedure or a
function refers to itself.
Indirect recursion – This is a form of recursion in which a function P
calls another function Q, and Q in turn calls P in its body.
Use of Recursion – We use recursion to make a complex code, simple.
Initially, the study of recursion and recursive algorithms seems to be
complicated but, once we identify the base process it becomes easy to
comprehend. Often, programming algorithms are written using recursion to
study it easily. In some cases recursion takes lots of programming time and
space. We use recursion only if the problem is recursively defined. Usually,
we use direct recursion more than indirect recursion.
Some practical uses of recursion are:
Disk directory trees navigation
Binary tree parsing
Searching
Sorting
Sorting is a real life example of recursion. We use lots of sorting algorithms
while programming. To enhance the efficiency of sorting algorithms we use
recursion. A sorting algorithm that includes recursion can have small
amount of coding in it and will decrease the man-hours required to write the
code.
Next, let us define recursive algorithms.
4.2.2 Recursive algorithms
Definition – An algorithm defined at least partially in terms of the same
algorithm is known as a recursive algorithm.
Recursive algorithms play a huge role in all the programming we do. It is not
necessary to use recursion for simple computational problems, but if we
N 0 1 2 3 4 5 6 7 8 9
Fn 0 1 1 2 3 5 8 13 21 34
{
return 1;
}
else
{
return fib(1)+fib(0) ;// return 1+1=2
}// this is a recursive function
}
We can see that the algorithm calls itself (fib()) to find the Fibonacci
numbers. It terminates when it reaches fib(0) and fib(1). The running time of
this recursive Fibonacci algorithm is given as T(n) = (Fn+1).
We will now discuss binary search, another recursive algorithm.
Binary search – Binary search is a recursive searching algorithm used to
look for an item in a sorted list of items.
Binary means two, and at each step, we are dividing the remaining list into
two parts. The basic idea of binary search is that we use two variables to
keep track of the endpoints of the range in the list where the item could be.
Initially, the target could be anywhere in the list, so we start with variables
low and high set to the first and last positions of the list, respectively.
We can use recursion to look for the middle value and then recursively
search either the lower half or the upper half of the list. The recursive loop
stops when either of the following conditions is met:
When the searched value is found
When there are no more values in the list to search
Examples for binary search are given below:
Example 1: Find 6 in [1, 2, 6, 17, 19, 25, 45, 76, 100, 112].
Step 1 (middle element is 19 > 6): 1 2 6 17 19 25 45 76 100 112
Step 2 (middle element is 2 < 6): 1 2 6 17
Step 3 (middle element is 6 == 6): 6 17
We can then recursively call the first function to start the search between
0 and len(nums)-1. Let us see the algorithm tracing for recursive binary
search.
This recursive binary search is easy to write and execute, but the binary
search using loops is a bit faster than this algorithm. The recursive versions
of binary search use a divide and conquer structure. Here, the efficiency of
algorithm lies in the way of searching an item. Instead of searching each
item individually, the binary search algorithm cuts the list into half every time
it calls itself again.
The Towers of Hanoi puzzle also uses a recursive algorithm. In the previous
unit we had discussed the non-recursive algorithm for the ‘Towers of Hanoi’
problem. Let us now discuss the recursive algorithm for this problem.
Towers of Hanoi – ‘Towers of Hanoi’ is a mathematical puzzle, which has
three towers or rods and a set of disks with different diameters. Initially the
disks are arranged one above the other, so that a smaller disc is placed
above a larger one. All the discs are to be transferred from the first tower
(Tower A) to the third tower (Tower C) with the help of the second tower
(Tower B) as a temporary storage.
The basic rules to be followed while transferring the discs are:
We can move only one disk at a time.
We cannot set aside the disk; it should be kept in another tower.
We cannot keep a larger disk on a smaller one.
The task is to move the disks from the first tower to the third tower.
Consider a simple example of this puzzle with three disks as shown in
figure 4.1. Let us label the towers as A, B, C. So the problem here is to
move the disks from A to C.
To solve this puzzle, we have to seven moves as shown in the figure 4.2.
The recursive algorithm for solving the towers of Hanoi puzzle with n
number of disks is given below:
Step 1 – Move n-disk tower from source to destination via resting place
Step 2 – Move n-1 disk tower from source to resting place
Step 3 – Move 1 disk tower from source to destination
Step 4 – Move n-1 disk tower from resting place to destination
The base case for this algorithm is when there is only one disk. Here we use
two recursive moves of n-1 disks and an additional move in step 3. As the
algorithm proceeds, the solvable number of disks in the puzzle is reduced.
Let us now define the recurrence relation for the towers of Hanoi puzzle.
The total number of moves required to solve the towers of Hanoi puzzle T[N]
is given in the equation 4.5.
T[N] ≤ T[N-1]+1+T[N-1] = 2T[N-1]+1 Eq: 4.5
Figure 4.3: Recursive Tree for Towers of Hanoi Puzzle with Four Disks
The number of steps for solving the puzzle increases with the number of
disks. The table 4.2 given below shows the number of steps required for
disks up to a number of 5. This is calculated using equation 4.2.
Disks 1 2 3 4 5
Steps 1 3 7 15 31
Even though we can write a simple algorithm to solve the towers of Hanoi
puzzle, it is considered as an intractable problem. This puzzle requires too
much of computing resources such as time and memory.
Analyzing efficiency of recursive algorithms
Let us see the general plan for analyzing the efficiency of recursive
algorithms. The steps are as follows:
1) Decide the size of the input based on a parameter n.
2) Identify and analyze the basic operations of the recursive algorithm.
3) Determine the number of times the basic operations are used. Check
whether the basic operations require more size than the decided input
size n.
4) Determine the best, worst and average cases for the input size n.
We have to analyze the cases separately if the basic operations depend
on it.
5) To solve the basic operation, set a recurrence relation.
6) Solve the recurrence relation using the forward and backward
substitution method. We can prove the solution using mathematical
induction.
These steps will be clearer if we discuss it using some examples.
Example 1: Computing factorial of a number n.
We can find the factorial of a number n by performing repeated
multiplication.
Consider n=5, then n factorial (n!) is computed by following steps:
1) n!= 5!
2) 4! * 5
3) 3! * 4 * 5
4) 2! * 3 * 4 * 5
5) 1! * 2 * 3 * 4 * 5
6) 0! * 1 * 2 * 3 * 4 * 5 * 6
7) 1 * 1 * 2 * 3 * 4 * 5 * 6
Step 4: The obtained recurrence relation M(n) = M(n-1) +1, can be solved
using forward and backward substitutions.
Forward substitution
M(1) = M(0) + 1 Eq: 4.8
M(2) = M(1) + 1= 1 + 1= 2 Eq: 4.9
M(3) = M(2) + 1 = 2 + 1=3 Eq: 4.10
In equations Eq: 4.8, Eq: 4.9, and Eq: 4.10, we are substituting value of n
and directly finding the value for M(n).
Backward substitution
M(n) = M(n-1) + 1
= [M(n-2)+1]+1 = M(n-2) + 2
= [M(n-3)+1]+1+1 = M(n-3) +2 Eq: 4.11
From the substitution method shown in equation Eq: 4.11, we can establish
a general formula as given in equation Eq: 4.12.
M(n) = M(n-i) + i Eq: 4.12
Let us now prove the correctness of the formula using mathematical
induction.
Prove: M(n)= n By mathematical induction
Let n=0 then
M(n) = 0
i.e. M(0) = 0 = n
Induction: if we assume M(n-1) = n-1 then
M(n) = M(n-1) + 1
= n -1+1
=n
i.e. M(n) = n
Precautions on recursion
In all cases recursion is not the only best way to solve a problem. We can
use recursion only when the problem is recursively defined. Any function
that is defined using recursion can also be defined using iteration. The time
taken to define a recursive function is more and some recursive method
calls can be difficult. Sometimes, recursion uses lots of space to solve the
problem. In such cases, a direct implementation of the problem works
better. Recursion should be used only if this direct implementation is very
complex to study.
The rules to be kept in mind while deciding on using recursion are:
We can use recursion if the problem definition requires a recursive
solution, provided it follows the next point.
We have to analyze the time and memory space of the recursive
solution. If we can solve it using a non recursive way, with lesser time
and space then go for it.
Do not use lots of recursion to solve a single problem. It becomes
complex to track every sub solution.
If we get a small and elegant recursive solution, then go for it.
A recursive function with its last line as a recursive call, does not waste
lots of memory. We can use this optimized way to write a recursive
function
Activity 1
Write an algorithm to calculate xn for different values and analyze its
efficiency.
We can leave the declaration part while measuring and analyze the
loop.
Case 1: if i = 0; frequency count is 1
Case 2: if i<n, frequency count= (n+1); Here the statement executes n
times for the condition i<n and executes once for the condition
i>n
Case 3: i++; frequency count is n
Case 4: sum= sum+b[i]; frequency count is n
Case 5: Return sum; frequency count is 1
Therefore, the total frequency count = (3n+3).After neglecting the
constant part, we can see the efficiency of the algorithm is O(n).
We can measure the time taken by the execution time of an algorithm
using the system clock.
But it is very difficult to calculate the system time for the following
reasons:
o It might not be accurate.
o It depends upon the type of computer. We can solve a problem in
limited time on a modern computer.
o If it is a time sharing system, it may include CPU execution time for
execution of the problem.
Figure 4.4 shows the algorithm visualization of bubble sort. The guidelines
for performing this visualization are as follows:
We can see the sorted portion of the list as a triangular block in the
bottom-right part of the image. We can use this to measure the
percentage of time taken.
We can track each element in the sort as the start and end of the
element is visible.
We can measure the percentage of list sorted at 20% and 80% of the
process.
Here we also see that the curve of the sorted elements is not linear, but
it is close to n2.
Example 2: Quick sort
Figure 4.5 shows the algorithm visualization of Quick Sort. The basic idea of
this algorithm visualization is to choose one element that we call pivot
(which is shown as the lines crossing in the centre) and to place all the
elements lower than the pivot on the left side and all the elements higher
than the pivot on the right side. This way the pivot is placed on the right
place and we repeat the same procedure for the two remaining sub lists and
sort the list recursively.
Activity 2
Write an algorithm to search an element from a list and illustrate using
the algorithm visualization features.
4.5 Summary
Let us summarize what we have discussed in this unit.
In this unit, we have defined recursion and recursive algorithms. We defined
recursion as something that calls itself to solve the problem easily.
Recursive function is any function that uses recursion in it. Next we
discussed the examples for recursive algorithms, namely. Fibonacci
numbers, binary search and the towers of Hanoi puzzle.
We also studied the steps for analyzing efficiency of recursive algorithm
using the example of finding the factorial of a number. Recursion should not
be used beyond a limit. We have also discussed when to avoid recursion.
We have discussed empirical analysis of algorithms which uses a set of
inputs to solve the problem. We explained the steps for empirical analysis
and its pros and cons. We have analyzed the process of algorithm
visualization which is the method of representing algorithms using images
and animations. We have also studied some illustrated examples of sorting
algorithms.
4.6 Glossary
Terms Description
Recurrence Recurrence relation is an equation that recursively defines a list
relation where each term of the list is defined as a function of the
preceding terms.
Induction Induction is a method of mathematical proof used to establish
that a given statement is true for all natural numbers.
4.8 Answers
Self Assessment Questions
1. Recursion
2. Recursive algorithms
3. 2n-1
4. Empirical analysis
5. Counters, system clocking
6. Pictorial
7. Algorithm visualization
8. Static algorithm
9. Dynamic algorithm
Terminal Questions
1. Refer section 4.2.2 – Towers of Hanoi
2. Refer section 4.2 – Analyzing efficiency of recursive algorithms
3. Refer section 4.2 – Precautions on recursion
4. Refer section 4.4.1 – Plan for empirical analysis of algorithms
5. Refer section 4.5 – Need for algorithm visualization
References
Corman. Thomas (1990). Introduction to algorithms. McGraw-Hill Book
Company
Liang, Daniel (2009). Introduction to Java Programming. Pearson
Eduction Ltd
Putembekar, A. A (2009). Design & Analysis of Algorithms. Technical
Publications
E-References
http://www.algolist.net
http://www.cargalmathbooks.com/5%20Recursive%20Algorithms.pdf
http://cse.unl.edu/~dsadofs/RecursionTutorial/index.php?s=intro#recfunc
http://www.devshed.com/c/a/Practices/Solving-Problems-with-Recursion
http://www.corte.si/posts/visualizingsorting/index.html
http://mila.cs.technion.ac.il/~yona/c/lecture12/slide19.html