To See Comments Click On Review and and Click On Show Comments
To See Comments Click On Review and and Click On Show Comments
To See Comments Click On Review and and Click On Show Comments
3
Overview of algorithm
• To apply algorithm first of all there should be a problem
that needs a solution
– Is there any problem for which we can’t design algorithm?
11
Properties of an algorithm: Finiteness
• Every valid algorithm must complete or terminate
after a finite number of steps.
• If you trace out the instructions of an algorithm,
then for all cases the algorithm must terminate
after a finite number of steps
– It must eventually stop either with the right output or
with a statement that no solution is possible
• Finiteness is an issue for computer algorithms
because
– Computer algorithms often repeat instructions
– If the algorithm doesn’t specify when to stop, the
computer will continue to repeat the instructions
forever 12
Why algorithm analysis ?
• There are many ways to solve a given problem
– So writing a working program to solve a problem
is not good enough
• The program may be inefficient and/or incorrect!
– If the program is run on a large data set, then the
running time becomes an issue
• Always we have to undertake algorithm analysis
before implementation
• Example: Selection Problem
– Given a list of N numbers, determine the kth
largest (or smallest) element in the list, where k N.
Example: Selection Problem
• Algorithm 1:
(1) Read N numbers into an array
(2) Sort the array in decreasing order by
some simple algorithm
(3) Return the element in kth position
Example: Selection Problem…
• Algorithm 2:
(1) Read the first k elements into an array and
sort them in decreasing order
(2) Each remaining element is read one by one
– If smaller than the kth element, then it is ignored
– Otherwise, it is placed in its correct spot in the
array, bumping one element out of the array.
(3) The element in the kth position is returned as
the answer.
Example: Selection Problem…
• Which algorithm is better when
– N =100 and k = 100?
– N =100 and k = 1?
17
Correct Algorithm
• A correct algorithm solves the given computational
problem
– If the algorithm is not doing what it is supposed to do, it is
worthless
• An algorithm is said to be correct if, for every input
instance, it halts with the correct output
• An incorrect algorithm
–might not halt at all on some input instances, or
–might halt with a wrong answer.
• In order to show that an algorithm is incorrect, you need
just one instance of its input for which the algorithm fails
• How to prove the correctness of an algorithm ?
– Common techniques are by mathematical induction & contradiction
18
Proof by Induction
• The induction base:
– is the proof that the statement is true for initial value (e.g. n =1)
• The induction hypothesis:
– is the assumption that the statement is true for an arbitrary values
1, 2, …, n
• The induction step:
– is the proof that if the statement is true for n, it must be true for
n+1
n 1
n n 1 1 0 r 1
r r ... r r
r 1
20
Example 2: Finding Maximum
• Finding the maximum element problem
–The Input is an array A storing n elements and the output is the
maximum one in A
• E.g.: Given array A=[31, 41, 26, 41, 58], max algorithm returns 58
26
Algorithm Analysis
Decide
Decideon on:algorithm
algorithm
design
designtechniques
techniquesetc.
Design an algorithm
Correctness
Prove correctness
Efficiency
Analyze efficiency
n
5. A[i+1] A[i] c5 t
j 2
j 1
n
6. i i-1 c6 t
j 2
j 1
7. A[i+1] key c7 n -1
(tj is the number of times the while loop in line 4 is executed for that value of j)
• The running time, T(n), of the insertion algorithm is the sum of running
times for each statement executed, i.e.:
=c1n+ c2(n-1)+ c3(n-1)+ c4nj=2 tj+ c5nj=2 (tj-1)+ c6nj=2(tj-1)+ c7(n-1)
Best Case Analysis of Insertion Sort
• Occurs if the array contains an already sorted values
–For each j = 2, 3, 4,…, n, we then find that A[i] ≤ key in
line 4 when i has its initial value of j – 1.
–Thus tj=1 for j = 2, 3,…, n, and line 5 and 6 will be
executed 0 times
• The best case running time is
T(n) = c1n + c2(n-1) + c3(n-1) + c4(n-1) + c7(n-1)
= (c1 + c2 + c3 + c4 + c7)n – (c2 + c3 + c4 + c7)
–This running time can be expressed as an + b for
constants a and b that depends on the statement cost c i;
• it is thus a linear function of n
Worst Case Analysis of Insertion Sort
• Occurs if the array contains values that are in reverse sorted
order, that is in decreasing order
• We must compare each element A[j] with each element in
the entire sorted subarray A[1..j-1]. So, t j = j for j = 2,3,…,n.
n n
n(n 1) n n
n(n 1)
t
j 2
j
j 2
j
2
1 (t j 1) ( j 1)
j 2 j 2 2
Therefore the worst case running time of INSERTION-SORT is T(n)
n(n 1) n(n 1) n(n 1)
c1n c2 (n 1) c3 (n 1) c4 ( 1) c5 ( ) c6 ( ) c7 (n 1)
2 2 2
c4 c5 c6 2 c4 c5 c6
( )n (c1 c2 c3 c7 )n (c2 c3 c4 c7 )
2 2 2 2
– This worst case running time can be expressed as an2 + bn + c for
constants a, b, c, it is thus a quadratic function on n
Average Case Analysis of Insertion Sort
• Suppose that we randomly choose n numbers and apply
insertion sort
• How long does it take to determine where in subarray
A[1..j-1] to insert the element A[j]?
– On average, half the elements in A[1..j-1] are less than A[j], and
half the elements are greater
– On average, therefore, we check half the subarray A[1..j-1], so tj =
j/2 and T(n) will still be in the order of n2,
• This average case running time can then be expressed as
quadratic function, an2 + bn + c for constants a, b, c, which is
the same as worst case
• In summary, the running time of insertion sort for
– Best case: an – b
– Worst case: an2 + bn - c
– Average case: an2 + bn - c
Asymptotic Analysis
• When analyzing the running time or space usage of
programs, we usually try to estimate the time or space as a
function of the input size.
– For example, when analyzing the worst case running time of an
insertion algorithm, we say the running time of insertion sort is,
T(n) = an2 + bn - c, for some constants a, b & c.
• The asymptotic behavior of a function f(n) refers to the
growth of f(n) as n gets large. We typically ignore small
values of n, since we are usually interested in estimating
how slow the program will be on large inputs.
– A good rule of thumb is: the slower the asymptotic growth rate,
the better the algorithm.
• By this measure, a linear algorithm; f(n)=an+c, is always
asymptotically better than a quadratic one; f(n)=an2+c. That
is because for any given (positive) a & c, there is always
some n at which the magnitude of an2+c overtakes an+c.
– For moderate values of n, the quadratic algorithm could very
well take less time than the linear one. However, the linear
algorithm will always be better for sufficiently large inputs.
Asymptotic analysis
51
Solution
1) Initialize start and end indexes
as start = 0, end = n-1
2) Swap arr[start] with arr[end]
3) Recursively call reverse
for rest of the array.
This is program answer
• • void printArray(int arr[], int size)
void rvereseArray(int arr[], int start, int end)
• {
• { • for (int i = 0; i < size; i++)
• • cout << arr[i] << " ";
if (start >= end)
•
• return; • cout << endl;
•
• }
•
• int temp = arr[start]; • /* Driver function to test above functions */
• int main()
• arr[start] = arr[end];
• {
• arr[end] = temp; • int arr[] = {1, 2, 3, 4, 5, 6};
•
•
• // To print original array
• // Recursive Function calling • printArray(arr, 6);
•
• rvereseArray(arr, start + 1, end - 1);
• // Function calling
• } • rvereseArray(arr, 0, 5);
•
•
• cout << "Reversed array is" << endl;
• •
• // To print the Reversed array
• /* Utility function to print an array */
• printArray(arr, 6);
•
• return 0;
• }
• void rvereseArray(int arr[], int • /* Utility function to print an array
start, int end) */
• { • void printArray(int arr[], int size)
• while (start < end) • {
• for (int i = 0; i < size; i++)
• {
• cout << arr[i] << " ";
• int temp = arr[start];
•
• arr[start] = arr[end]; • cout << endl;
• arr[end] = temp; • }
• start++; •
• end--; • /* Driver function to test above
• } functions */
• }
• int main()
• {
• int arr[] = {1, 2, 3, 4, 5, 6};
•
• int n = sizeof(arr) / sizeof(arr[0]);
•
• // To print original array
• printArray(arr, n);
•
• // Function calling
• rvereseArray(arr, 0, n-1);
•
• cout << "Reversed array is" << endl;
•
• // To print the Reversed array
• printArray(arr, n);
•
• return 0;
• }
Algorithmic Paradigms
Algorithmic Paradigms
•Techniques for Algorithms Design:
–General approaches to the construction of efficient solutions
to problems.
•Such methods are of interest because:
–They provide templates suited to solve a broad range of
diverse problems which can be precisely analyzed.
–They can be translated into common control and data
structures provided by most high-level languages.
•Although more than one technique may be applicable
to a specific problem, it is often the case that an
algorithm constructed by one approach is clearly
superior to equivalent solutions built using alternative
techniques.
–The choice of design paradigm is an important aspect of
algorithm analysis
Algorithmic Paradigms
• Some of the techniques for the Design of Algorithms
include:
– Divide and Conquer
– Greedy
– Dynamic Programming
– Backtracking
– Branch and bound, ….
» t
The divide-and-conquer
strategy
Divide-and-Conquer Strategy
• This is a general algorithm design paradigm that has
created such efficient algorithms as Max-Min, Merge
Sort, Binary Search, ….
• This method has three distinct steps:
– Divide: If the input size is too large, divide the input into two
or more sub-problems. That is, divide P P1, …, Pk
• If the input size of the problem is small, it is solved directly
– Recur: Use divide and conquer to solve the sub-problems
associated with each one-kth of the data subsets separately,
That is, find solution for S(P1), …, S(Pk)
– Conquer: Take the solutions to the sub-problems and
combine (“merge”) these solutions into a solution for the
original problem. That is, Merge S(P1 ), …, S(Pk) S(P)
The Divide and Conquer Strategy
• Implementation: suppose we consider the divide-
and-conquer strategy when it splits the input into two
sub-problems of the same kind as the original
problem.
• If the input size of the problem is small, it is solved
directly.
• If the input size of the problem is large, apply the
strategy:
– Divide: divide the input data S in two disjoint subsets S1and
S2
– Recur: Solve each half of the sub-problems associated with
S1 and S2
– Conquer: combine the solution for S1and S2 into a solution
for S
General Algorithm
procedure DCS (P)
if small(P) then
return S(P)
else
divide P into smaller instances P1, P2 …, Pk
apply DCS to each of these sub-problems
return (combine(DCS(P1), DCS(P2), …, DCS(Pk))
end if;
end DCS;
Complexity: T(n) = f(n) n small
aT(n/b) + g(n) otherwise, where
• b be the ways we divide the problem at each step
• a be the number of sub-problems we solve at each step; i.e. n/b.
• T(n) be the time needed to solve the problem with input of size n
• g(n) be the time for dividing the problem & combining solutions to sub-
problems
• f(n) be the time to compute the answer directly for small inputs
Divide-and-Conquer Technique
a problem of size n
(instance)
subproblem 1 subproblem 2
of size n/2 of size n/2
a solution to a solution to
subproblem 1 subproblem 2
a solution to
the original problem
In general it leads to a recursive algorithm with complexity
T(n) = 2 T(n/2) + g(n)
Solving Recurrence Relation
• One of the method for solving recurrence relation is
called the substitution method.
–This method repeatedly makes substitutions for each
occurrence of the function T(n) until all such occurrences
disappear
1 n2
2.T(n) = 2T(n/2)+1 n>2
Example of Recursion: SUM A[1…n]
•Problem: Write a recursive function to find the sum of the first
n integers A[1…n] and output the sum
–Example: given k = 3, we return sum = A[1] + A[2] + A[3]
given k = n, we return A[1] + A[2] + … + A[n]
–How can you define the problem in terms of a smaller
problem of the same type?
1 + 2 + … + n = [1 + 2 + … + (n -1)] + n
for n > 1, f(n) = f(n-1) + n
–How does each recursive call diminish the size of the
problem? It reduces by 1 the number of values to be
summed.
–What instance of the problem can serve as the base case?
n=1
–As the problem size diminishes, will you reach this base
case? Yes, as long as n is nonnegative. Therefore the
statement “n >= 1” needs to be a precondition
Example of Recursion : SUM A[1…n]
Problem: Write a recursive function to find the sum of
the first n integers A[1…n] and output the sum
algorithm LinearSum(A, n)
// Input: an array A with n elements
// Output: The sum of the first n integers in A
if n = 1 then call return 15 + A [4 ] = 15 + 5 = 20
return A[0]
LinearSum ( A ,5)
else
return LinearSum(A, n - 1) + A[n] call return 13 + A [3 ] = 13 + 2 = 15
call return 7 + A [2 ] = 7 + 6 = 13
LinearSum ( A ,3)
call return A [ 0 ] = 4
LinearSum (A , 1)
Binary Sum
• Binary sum occurs whenever there are two recursive calls for
each non-base case.
Algorithm BinarySum(A, i, n):
//Input: An array A and integers i and n
//Output: The sum of the n integers in A starting at index i
if n = 1 then
return A[i ]
return (BinarySum(A, i, n/ 2) + BinarySum(A, i + n/ 2, n/ 2))
0, 8
end algorithm
0, 4 4, 4
0, 2 2, 2 4, 2 6, 2
0, 1 1, 1 2, 1 3, 1 4, 1 5, 1 6, 1 7, 1
Binary search
• Binary Search is an algorithm to find an item in a sorted list.
–very efficient algorithm for searching in sorted array
–Limitations: must be a sorted array
• Problem: determine whether a given element K is present in
the given list or not
–Input: Let A = <a1, a2, … an> be a list of elements that are sorted in non-
decreasing order.
–Output: If K is present output its position. Otherwise output “Not Found”.
• Implementation:
–Pick the pivot item in the middle: Split the list in two halves (size n/2) at m
so that
A[1], … A[m], … A[n].
–If K = A[m], stop (successful search);
–Otherwise, until the list has shrunk to size 1 narrow our search
recursively to either
the top half of the list : A[1..m-1] if K < A[m] or
the bottom half of the list: A[m+1..n] if K > A[m]
Example
• Example: Binary Search for 64 in the given list A[1:17] =
{5 8 9 13 22 30 34 37 38 41 60 63 65 82 87 90 91}
1. Looking for 64 in this list.
2. Divide the list into two
(1+17)/2 = 9
3. Pivot = 38. Is 64 < 38?
No.
4. Recurse looking for 64 in Pivot
the list > 38.
5. etc.
Central office
9 9
2 2
1 2 6 1 2 6
4 4
4 5 4 5
5 4 5 4
5 5 5 5
3 3
Prim’s Algorithm
procedure primMST(G, cost, n, T)
Pick a vertex 1 to be the root of the spanning tree T
mincost = 0
for i = 2 to n do near (i) = 1
near(1) = 0
for i = 1 to n-1 do
find j such that near(j) ≠ 0 and cost(j,near(j)) is min
T(i,1) = j; T(i,2) = near (j)
mincost = mincost + cost(j,near(j))
near (j) = 0
for k = 1 to n do
if near(k) ≠ 0 and cost(k,near(k) > cost(k,j) then
near (k) = j
end for
end for
return mincost
end procedure
Correctness of Prim’s
• If the algorithm is correct it halts with the right
answer or optimal solution.
• Optimal solution is obtained if:
• Prim algorithm adds n-1 edges (with
minimum cost) to the spanning tree without
creating a cycle
• Proof that PRIM algorithm creates a minimum
spanning tree of any connected graph.
• Prove by contradiction
• Suppose it wasn't.
• Proof. Let T be the spanning tree returned
by the algorithm, and suppose there
doesn’t exist any MST of G consistent with
T. Consider an optimal MST O of G.
Kruskal’s algorithm
• Kruskal algorithm: Always tries the lowest-cost remaining edge
• It considers the edges of the graph in increasing order of cost.
• In this approach, the set T of edges so far selected for the
spanning tree may not be a tree at all stages in the algorithm.
But it is possible to complete T into a tree.
• Create a forest of trees from the vertices
• Repeatedly merge trees by adding “safe edges” until only
one tree remains. A “safe edge” is an edge of minimum
weight which does not create a cycle
• Example:
9 b
a 2 6 Initially there is a forest:
d {a}, {b}, {c}, {d}, {e}
4 5
5 4
2 5 13
S B E T
16 2
5
C 2
F
It takes O(n,m)
def lcs(s1, s2):
matrix = [["" for x in range(len(s2))] for x in range(len(s1))]
for i in range(len(s1)):
for j in range(len(s2)):
if s1[i] == s2[j]:
if i == 0 or j == 0:
matrix[i][j] = s1[i]
else:
matrix[i][j] = matrix[i-1][j-1] + s1[i]
else:
matrix[i][j] = max(matrix[i-1][j], matrix[i][j-1], key=len)
cs = matrix[-1][-1]
return len(cs), cs
str1 = input("enter first string")
str2 = input("enter second string")
print(lcs(str1, str2))
Example
Transform the sequences
Xi = {a a b a b} into Yj = {b a b b}
with minimum cost sequence of edit operation using
dynamic programming approach, Assume that
change costs 2 units, delete and insert 1 unit.
j 0 1 2 3 4 The value 3 at (5,4) is the
i
0 0 1 2 3 4 optimal solution
1 1 2 1 2 3 By tracing back one can
determine which operations
2 2 3 2 3 4 lead to optimal solution
3 3 2 3 2 3 • Delete x1, Delete x2 and
4 4 3 2 3 4 Insert y4 Or,
5 5 4 3 2 3 • Change x1 to y1 & Delete x4