Design A Algorithm
Design A Algorithm
Recurrence Equations
The recurrence equation is an equation that defines a sequence recursively .It is normally in
the form
T(n) = T(n-1) + n for n>0 (Recurrence relation)
T(0) = 0 (Initial condition)
The general solution to the recursive function specifies some formula.
Consider , a recurrence relation T(n) = T(n-1) + n with initial condition T(0) = 0 ------ (1)
Solution:
If k = n in equ(4) then
T(n) = = /2 +
T(n) = O(n2)
Example : 2
T(n) = T(n-1) + 1 with initial condition with T(0) = 0 . Find big oh notation.
Solution:
T(n-1) = T(n-2)+1
T(n-2) = T(n-3) + 1
So
2
If k = n then eqa(4) becomes
T(n ) = T(0) + n = 0 + n = n
T(n) = O(n)
Example 3:
T(n/2) =
T(n/4) =
T(n) = 23T(n/23)+3n
In general
Assume 2k = n
T(n) = n.T(n/n)+logn.n
=n.T(1) + n.logn
T(n) = n + n.logn
Example 4:
Solution :
3
T(n) = T(n/3) + C ----------- (1)
T(n/3) = T(n/9)+C
T(n/9) = T(n/27) + C
T(n) = [T(n/27)+C] + 2C
T(n) = T(n/27) + 3C
In General
T(n) = T(n/3k) + kC
Put 3k = n then
T(n) = T(n/n)+log3n.C
= T(1) + log3n.C
T(n) = C. log3n + 1
Tree Method
In this method, we buit a recurrence tree in which each node represents the cost of a single
sub problemin the form of recursive function invocations.Then we sum up the cost at each
levelto determine the overall cost.Thus the recursion tree helps us to make a good guess of
time complexity. The pattern is typically a arithmetic or geometric series.
cn2
/ \
T(n/4) T(n/2)
4
cn2
/ \
c(n2)/16 c(n2)/4
/ \ / \
cn2
/ \
c(n2)/16 c(n2)/4
/ \ / \
/ \ / \ / \ / \
Example :
5
Time complexity of above tree is O(n2)
Let's consider another example,
T(n) = T(n/3) + T(2n/3) + n.
Expanding out the first few levels, the recurrence tree is:
Then the master theorem can be stated for efficiency analysis as:
A = 1, b = 2, d= 2
A = 2, b = 4, d = ½
A = 3 , b = 2, d = 1
T(n) = ϴ(nlogba)
T(n) = ϴ(nlogbalogn)
T(n) = ϴ(f(n))
Steps:
7
(i) Get the values of a,b and f(n)
(ii) Determine the value nlogba
(iii)Compare f(n) and nlogba
Example : 1
T(n) = 2T(n/2)+n
A = 2, b = 2, f(n) = n
Example : 2:
T(n) = 9T(n/3) + n
A = 9 , b =3,f(n) = n
F(n) = n
Example : 3:
A = 3, b = 4, f(n) = nlogn
Example 4:
T(n) = 3T(n/2) + n2
A = 3, b = 2, f(n) = n2
Example 5:
T(n) = 4T(n/2) + n2
A = 4, b = 2, f(n) = n2
F(n) = n2 case 2:
Example 6:
A = 4 , b = 2 ,f(n) = n/logn
Example 7 :
A = 6 , b = 3 , f(n) = n2logn
T(n) = ϴ(n2)
T(n) = 7T(n/3) + n2
9
T(n) = 4T(n/2) + logn
T(n) = 16T(n/4) + n
In the worst case analysis, we calculate upper bound on running time of an algorithm. We
must know the case that causes maximum number of operations to be executed. For Linear
Search, the worst case happens when the element to be searched (x in the above code) is not
present in the array or the search element is present at nth location. For these cases, the
search() functions compares it with all the elements of arr[] one by one. Therefore, the worst
case time complexity of linear search would be O(n).
Average case complexity gives information about the behaviour of an algorithm on a random
input. Let us understand some terminologies that are required for computing average case
time complexity.
Let the algorithm is for linear search and P be a probability of getting successful search.
N is the total number of elements in the list.
10
The first match of the element will occur at ith location. Hence the probability of occurring
first match is P/n for every ith element.
The probability of getting unsuccessful search is (1-P).
Now, we can find average case time complexity Ɵ (n) as-
Ɵ (n) =probability of successful search+ probability of unsuccessful search
[ ]
Ɵ (n) = 1.P/n+2.P/n+...+i.P/n+...n.P/n +n. (1-P) //There may be n elements at
which chances of not getting element are possible. Hence n. (1-P)
Thus we can obtain the general formula for computing average case time complexity.
Suppose if P=0 that means there is no successful search i.e. we have scanned the entire list of
n elements and still we do not found the desired element in the list then in such a situation ,
Ɵ (n) =O (n+1) / 2+n (1-0)
Ɵ (n) = n
That means the algorithm scans about half of the elements from the list.
Thus computing average case time complexity is difficult than computing worst case and best
case time complexities.
Best Case Analysis (omega)
In the best case analysis, we calculate lower bound on running time of an algorithm. We must
know the case that causes minimum number of operations to be executed. In the linear search
problem, the best case occurs when x is present at the first location. The number of operations
in the best case is constant (not dependent on n). So time complexity in the best case would
be Ω (1).
The decision tree for insertion sort operating on three elements. There are 3! = 6 possible
permutations of the input elements, so the decision tree must have at least 6 leaves.
The decision-tree model
Comparison sorts can be viewed abstractly in terms of decision trees. A decision tree
represents the comparisons performed by a sorting algorithm when it operates on an input of
a given size. Control, data movement, and all other aspects of the algorithm are ignored. The
above figure shows the decision tree corresponding to the insertion sort algorithm for an input
sequence of three elements.
In a decision tree, each internal node is annotated by ai : aj for some i and j in the range
1 i, j n, where n is the number of elements in the input sequence. Each leaf is annotated by
a permutation (1), (2), . . . , (n) . The execution of the sorting algorithm corresponds
to tracing a path from the root of the decision tree to a leaf. At each internal node, a
comparison ai aj is made. The left subtree then dictates subsequent comparisons for ai aj,
and the right subtree dictates subsequent comparisons for ai > aj. When we come to a leaf, the
sorting algorithm has established the ordering a (1) a (2) . . . a (n). Each of the n!
permutations on n elements must appear as one of the leaves of the decision tree for the
sorting algorithm to sort properly.
A lower bound for the worst case
The length of the longest path from the root of a decision tree to any of its leaves represents
the worst-case number of comparisons the sorting algorithm performs. Consequently, the
worst-case number of comparisons for a comparison sort corresponds to the height of its
decision tree. A lower bound on the heights of decision trees is therefore a lower bound on
12
the running time of any comparison sort algorithm. The following theorem establishes such a
lower bound.
Theorem
Any decision tree that sorts n elements has height (n lg n).
Proof Consider a decision tree of height h that sorts n elements. Since there are n!
permutations of n elements, each permutation representing a distinct sorted order, the tree
must have at least n! leaves. Since a binary tree of height h has no more than 2h leaves, we
have
n! 2h ,
which, by taking logarithms, implies
h lg(n!) ,
since the lg function is monotonically increasing. From Stirling's approximation, we have
Radix Sort
The idea of Radix Sort is to do digit by digit sort starting from least significant digit to most
significant digit. Radix sort uses counting sort as a subroutine to sort. Radix sort iteratively
orders all the strings by their nth character – in the first iteration, the strings are ordered by
their last character. In the second run, the strings are ordered in respect to their penultimate
character. And because the sort is stable, the strings, which have the same penultimate
character, are still sorted in accordance to their last characters. After nth run the strings are
sorted in respect to all character positions.
0
1 710,812, 715
2
3 437
4 340
5
6
7
8 582,,385
9 493, 195
Now the sub lists are gathered in order from 0 to 9:
710, 812, 715, 437, 340, 582,385, 493,195
Finally, the sub lists are created according to the hundred's digit:
Digit Sub list
0
1 195
2
3 340, 385
4 437, 493
5 582
6
7 710 ,715
8 812
9
At last, the list is gathered up again:
195, 340, 385,437,493,582,710,715,812
And now we have a fully sorted array! Radix Sort is very simple, and a computer can do it
fast. When it is programmed properly, Radix Sort is in fact one of the fastest sorting
algorithms for numbers or strings of letters.
Radix-Sort(A, d)
// Each key in A[1..n] is a d-digit integer.
(Digits are // numbered 1 to d from right to left.)
for i = 1 to d do
Use a stable sorting algorithm to sort A on digit i.
Another version of Radix Sort Algorithm
Algorithm RadixSort(a,n)
{
m = Max(a,n)
d = Noofdigit(M)
Make all the element are having “d” number of digit
for(i=1;i<=d,i++)
{
for(r=0; r<= 9; r++)
count[r] = 0;
14
for(j =1;j<=n;j++)
{
p= Extract(a[j],i);
b[p][count[p]] = a[j];
count[p]++;
}
s =1;
for(t=0;t<=9; t++)
{
for(k=0;k<count[t];k++)
{
a[s] = b[t][k];
s++;
}
}
}
print “ Sorted list”
}
In the above algorithm assume Max(a,n) is a method used to find out the maximum number
in the array, Noofdigit(M) is a method used to find out the number of digit in ‘M’ and
Extract(a[j],i) is a method used to extract the digit from a[j] based on i value (i.e if i value is 1
extract first digit , if i value is 2 extract second digit, if i value is 3 extract third digit from
right to left ) . Count[] is an array which contains the number of elements available in each
row and in each iteration. The number of time i ‘for’ loop is executed is Depending on the
value of ‘d’, i for loop is repeated.
Disadvantages
Still, there are some tradeoffs for Radix Sort that can make it less preferable than other sorts.
The speed of Radix Sort largely depends on the inner basic operations, and if the operations
are not efficient enough, Radix Sort can be slower than some other algorithms such as Quick
Sort and Merge Sort. These operations include the insert and delete functions of the sub lists
and the process of isolating the digit you want.
In the example above, the numbers were all of equal length, but many times, this is not the
case. If the numbers are not of the same length, then a test is needed to check for additional
digits that need sorting. This can be one of the slowest parts of Radix Sort, and it is one of the
hardest to make efficient.
Analysis
Worst case complexity O(d *n)
Average case complexity ϴ( d* n).
Best Case Complexity Ω( d * n )
Let there be d digits in input integers. Radix Sort takes O(d*(n+b)) time where b is the base
for representing numbers, for example, for decimal system, b is 10. What is the value of d? If
k is the maximum possible value, then d would be O(logb(k)). So overall time complexity is
O((n+b) * logb(k)). Which looks more than the time complexity of comparison based sorting
algorithms for a large k. Let us first limit k. Let k <= nc where c is a constant. In that case, the
complexity becomes O(nLogb(n)). But it still doesn’t beat comparison based sorting
algorithms.
15
What if we make value of b larger?. What should be the value of b to make the time
complexity linear? If we set b as n, we get the time complexity as O(n). In other words, we
can sort an array of integers with range from 1 to nc if the numbers are represented in base n
(or every digit takes log2(n) bits).
Bucket Sort:
Bucket sort (bin sort) is a stable sorting algorithm based on partitioning the input array into
several parts – so called buckets – and using some other sorting algorithm for the actual
sorting of these sub problems.
At first algorithm divides the input array into buckets. Each bucket contains some range of
input elements (the elements should be uniformly distributed to ensure optimal division
among buckets).In the second phase the bucket sort orders each bucket using some other
sorting algorithm, or by recursively calling itself – with bucket count equal to the range of
values, bucket sort degenerates to counting sort. Finally the algorithm merges all the ordered
buckets. Because every bucket contains different range of element values, bucket sort simply
copies the elements of each bucket into the output array (concatenates the buckets).
BUCKET SORT (a,n)
n ← length [a]
m = Max(a,n)
nob = 10 // Number of backet
divider = ceil((m+1)/nob);
for i = 1 to n do
{
j = floor(a[i]/divider)
b[j] = a[i]
}
for i = 0 to 9 do
sort b[i] with Insertion sort
concatenate the lists B[0], B[1], . . B[9] together in order.
End Bucket Sort
Example :
a = { 123,67,45,3,69,245,35,90}
n= 8
max = 245
nob = 10 ( No of backet)
divider = ceil((m+1)/nob) = ceil((245+1)/nob)
= ceil(246/10) = ceil(24.6) = 25
j = floor(125/25) = 5 , so b[5] = 125
j = floor(67/25) = floor(2.68) = 2 , so b[2] = 67
j = floor(45/25) = floor(1.8) = 1 , so b[1] = 45
j = floor(3/25) = floor(0.12) = 0 , so b[0] = 3
j = floor(69/25) = floor(2.76) = 2 , so b[2] = 69
j = floor(245/25) = floor(9.8) = 9 , so b[9] = 245
j = floor(35/25) = floor(1.4) = 1 , so b[1] = 35
j = floor(90/25) = floor(3.6) = 3, so b[3] = 90
0 3
1 45 35
16
2 67 69
3 90
4
5 125
6
7
8
9 245
0 3
1 35 45
2 67 69
3 90
4
5 125
6
7
8
9 245
17
Counting sort determines for each input element x, the number of elements less than
x. And it uses this information to place element x directly into its position in the output array.
Consider the input set : 4, 1, 3, 4, 3. Then n=5 and k=4
∈
The algorithm uses three array:
Input Array: A[1..n] store input data where A[j] {1, 2, 3, …, k}
Output Array: B[1..n] finally store the sorted data
Temporary Array: C[1..k] store data temporarily
Counting Sort Example
Example 1 :
Given List :
A = { 2,5,3,0,2,3,0,3}
Step:1
A
1 2 3 4 5 6 7 8
2 5 3 0 2 3 0 3
C- Highest Element is 5 in the given array
0 1 2 3 4 5
0 0 0 0 0 0
B-Output Array
Step:2
C[A[J]]=C[A[J]]+1
C[A[1]]=C[2]=C[2]+1 . In the place of C[2] add 1.
0 1 2 3 4 5
0 0 1 0 0 0
0 1 2 3 4 5
2 0 2 3 0 1
Step:4 C[i]=C[i]+C[i-1]
C 0 1 2 3 4 5
2 0 2 3 0 1
Intially C[0]=C[0]
=2
C[1]=C[0]+C[1]
=2+0 =2
C[2]=C[1]+C[2]
=2+2 =4
18
0 1 2 3 4 5
2 2 4 7 7 8
Sorted List: B
B[C[A[j]]] ←A[j]
C[A[j]] ←C[A[j]]-1
J=8 to 1
B[C[A[8]]]= A[8]
B[7]=3
1 2 3 4 5 6 7 8
B[C[A[7]]]= A[7]
B[2]=0
1 2 3 4 5 6 7 8
0 3
0 0 2 2 3 3 3 5
Algorithm
Counting-sort(A,B,K)
{
for i←0 to k
{
C[i] ←0
}
for j ←1 to length[A]
{
C[A[j]] ←C[A[j]]+1
}
// C[i] contains number of elements equal to i.
for i ←1 to k
{
C[i]=C[i]+C[i-1]
}
// C[i] contains number of elements ≤ i.
for j ←length[A] downto 1
{
19
B[C[A[j]]] ←A[j]
C[A[j]] ←C[A[j]]-1
}
}
Analysis of COUNTING-SORT(A,B,k)
Counting-sort(A,B,k)
{
for i←0 to k Θ(k)
{
C[i] ←0
}
for j ←1 to length[A] Θ(n)
{
C[A[j]] ←C[A[j]]+1
}
// C[i] contains number of elements equal to i.
for i ←1 to k Θ(k)
{
C[i]=C[i]+C[i-1]
}
// C[i] contains number of elements ≤ i.
for j ←length[A] downto 1 Θ(n)
{
B[C[A[j]]] ←A[j]
C[A[j]] ←C[A[j]]-1
}
}
Complexity
How much time does counting sort requires?
• For loop of lines 1-2 takes time ϴ(k).
• For loop of lines 3-4 takes time ϴ(n).
• For loop of lines 6-7 takes time ϴ(k).
• For loop of lines 9-11 takes time ϴ(n).
Thus the overall time is ϴ(k+n).In practice we usually use counting sort when we
have k =O(n), in which the running time is ϴ(n).
Worst Case Complexity is O(n)
Average Case Complexity is ϴ(n).
Best Case = Ω(n)
20