Unit 1 Notes Wordgcr
Unit 1 Notes Wordgcr
UNIT I INTRODUCTION
Problem to be solved
Algorithm
It is a step by step procedure with the input to solve the problem in a finite amount of time
to obtain the required output.
Characteristics of an algorithm:
Input: Zero / more quantities are externally
supplied. Output: At least one quantity is produced.
Definiteness: Each instruction is clear and unambiguous.
Finiteness: If the instructions of an algorithm is traced then for all cases the algorithm must
terminates after a finite number of steps.
Efficiency: Every instruction must be very basic and runs in short time.
Example:
The greatest common divisor(GCD) of two nonnegative integers m and n (not-both-zero),
denoted gcd(m, n), is defined as the largest integer that divides both m and n evenly, i.e., with a
remainder of zero.
Euclid’s algorithm is based on applying repeatedly the equality gcd(m, n) = gcd(n, m mod n),
where m mod n is the remainder of the division of m by n, until m mod n is equal to 0. Since gcd(m,
0) = m, the last value of m is also the greatest common divisor of the initial m and n.
gcd(60, 24) can be computed as follows:gcd(60, 24) = gcd(24, 12) = gcd(12, 0) = 12.
approximate answer. E.g., extracting square roots, solving nonlinear equations, and
evaluating definite integrals.
(c) Algorithm Design Techniques
An algorithm design technique (or “strategy” or “paradigm”) is a general approach
to solving problems algorithmically that is applicable to a variety of problems from
different areas of computing.
Algorithms+ Data Structures = Programs
Though Algorithms and Data Structures are independent, but they are combined
together to develop program. Hence the choice of proper data structure is required
before designing the algorithm.
Implementation of algorithm is possible only with the help of Algorithms and Data
Structures
Algorithmic strategy / technique / paradigm are a general approach by which
many problems can be solved algorithmically. E.g., Brute Force, Divide and
Conquer, Dynamic Programming, Greedy Technique and so on.
Algorithm Specification
Pseudocode and flowchart are the two options that are most widely used nowadays for specifying
algorithms.
a. Natural Language
It is very simple and easy to specify an algorithm using natural language. But many times specification
of algorithm by using natural language is not clear and thereby we get brief specification.
Example: An algorithm to perform addition of two numbers.
Step 1: Read the first number, say a.
Step 2: Read the first number, say b.
Step 3: Add the above two numbers and store the result in c.
Step 4: Display the result from c.
Such a specification creates difficulty while actually implementing it. Hence many programmers prefer
to have specification of algorithm by means of Pseudocode.
b. Pseudocode
Pseudocode is a mixture of a natural language and programming language constructs.
Pseudocode is usually more precise than natural language.
For Assignment operation left arrow “←”, for comments two slashes “//”,if condition, for,
while loops are used.
ALGORITHM Sum(a,b)
//Problem Description: This algorithm performs addition of two numbers
//Input: Two integers a and b
//Output: Addition of two integers
c←a+b
return c
This specification is more useful for implementation of any language.
c. Flowchart
In the earlier days of computing, the dominant method for specifying algorithms was a flowchart,
this representation technique has proved to be inconvenient.
Flowchart is a graphical representation of an algorithm. It is a a method of expressing an algorithm
by a collection of connected geometric shapes containing descriptions of the algorithm’s steps.
Symbols Example: Addition of a and b
Transition / Assignment
Input the value of a
Once an algorithm has been specified then its correctness must be proved.
An algorithm must yields a required result for every legitimate input in a finite amount of
time.
For example, the correctness of Euclid’s algorithm for computing the greatest common
divisor stems from the correctness of the equality gcd(m, n) = gcd(n, m mod n).
A common technique for proving correctness is to use mathematical induction because an
algorithm’s iterations provide a natural sequence of steps needed for such proofs.
The notion of correctness for approximation algorithms is less straightforward than it is for
exact algorithms. The error produced by the algorithm should not exceed a predefined
limit.
(i) Sorting
The sorting problem is to rearrange the items of a given list in nondecreasing (ascending)
order.
Sorting can be done on numbers, characters, strings or records.
To sort student records in alphabetical order of names or by student number or by student
grade-point average. Such a specially chosen piece of information is called a key.
An algorithm is said to be in-place if it does not require extra memory, E.g., Quick sort.
A sorting algorithm is called stable if it preserves the relative order of any two equal
elements in its input.
(ii) Searching
The searching problem deals with finding a given value, called a search key, in a given set.
E.g., Ordinary Linear search and fast binary search.
TABLE 1.1 Values (approximate) of several functions important for analysis of algorithms
n √𝑛 log2n n n log2n n2 n3 2n n!
1 1 0 1 0 1 1 2 1
2 1.4 1 2 2 4 4 4 2
4 2 2 4 8 16 64 16 24
8 2.8 3 8 2.4•101 64 5.1•102 2.6•102 4.0•104
10 3.2 3.3 10 3.3•101 102 103 103 3.6•106
16 4 4 16 6.4•101 2.6•102 4.1•103 6.5•104 2.1•1013
10 2 10 6.6 10 2
6.6•102 10 4
10 6
1.3•1030 9.3•10157
10 3 31 10 10 3
1.0•10 4
10 6
10 9
In the worst case, there is no matching of elements or the first matching element can found
at last on the list. In the best case, there is matching of elements at first on the list.
Worst-case efficiency
The worst-case efficiency of an algorithm is its efficiency for the worst case input of size n.
The algorithm runs the longest among all possible inputs of that size.
For the input of size n, the running time is Cworst(n) = n.
Yet another type of efficiency is called amortized efficiency. It applies not to a single run of
an algorithm but rather to a sequence of operations performed on the same data structure.
Asymptotic notation is a notation, which is used to take meaningful statement about the
efficiency of a program.
The efficiency analysis framework concentrates on the order of growth of an algorithm’s
basic operation count as the principal indicator of the algorithm’s efficiency.
To compare and rank such orders of growth, computer scientists use three notations, they
are:
O - Big oh notation
Ω - Big omega notation
Θ - Big theta notation
Let t(n) and g(n) can be any nonnegative functions defined on the set of natural numbers.
The algorithm’s running time t(n) usually indicated by its basic operation count C(n), and g(n),
some simple function to compare with the count.
above by some constant multiple of g(n) for all large n, i.e., if there exist some positive constant c
Where t(n) and g(n) are nonnegative functions defined on the set of natural numbers.
O = Asymptotic upper bound = Useful for worst case analysis = Loose bound
A function t(n) is said to be in Ω(g(n)), denoted t(n) ∈ Ω(g(n)), if t(n) is bounded below by
(ii) Ω - Big omega notation
some positive constant multiple of g(n) for all large n, i.e., if there exist some positive constant c
and some nonnegative integer n0 such that
t (n) ≥ cg(n) for all n ≥ n0.
Where t(n) and g(n) are nonnegative functions defined on the set of natural numbers.
Ω = Asymptotic lower bound = Useful for best case analysis = Loose bound
A function t(n) is said to be in Θ(g(n)), denoted t(n) ∈ Θ(g(n)), if t(n) is bounded both
(iii) Θ - Big theta notation
above and below by some positive constant multiples of g(n) for all large n, i.e., if there exist some
positive constants c1 and c2 and some nonnegative integer n0 such that
c2g(n) ≤ t (n) ≤ c1g(n) for all n ≥ n0.
Where t(n) and g(n) are nonnegative functions defined on the set of natural numbers.
Θ = Asymptotic tight bound = Useful for average case analysis
1 𝑛 (𝑛 − 1) = 1 𝑛2 − 1 𝑛 ≤ 1 𝑛2 for all n ≥ 0.
Proof: First prove the right inequality (the upper bound):
2 2 2 2
1 𝑛 (𝑛 − 1) = 1 𝑛2 − 1 𝑛 ≥ 1 𝑛2 − [1 𝑛] [1 𝑛] for all n ≥ 2.
Second, we prove the left inequality (the lower bound):
2 2 2 2 2 2
THEOREM: If t1(n) ∈ O(g1(n)) and t2(n) ∈ O(g2(n)), then t1(n) + t2(n) ∈ O(max{g1(n),
g2(n)}). (The analogous assertions are true for the Ω and Θ notations as well.)
PROOF: The proof extends to orders of growth the following simple fact about four arbitrary real
Since t1(n) ∈ O(g1(n)), there exist some positive constant c1 and some nonnegative integer
numbers a1, b1, a2, b2: if a1 ≤ b1 and a2 ≤ b2, then a1 + a2 ≤ 2 max{b1, b2}.
n1 such that
Hence, t1(n) + t2(n) ∈ O(max{g1(n), g2(n)}), with the constants c and n0 required by
the definition O being 2c3 = 2 max{c1, c2} and max{n1, n2}, respectively.
EXAMPLE 1: Compute the factorial function F(n) = n! for an arbitrary nonnegative integer n.
Since n!= 1•. . . . • (n − 1) • n = (n − 1)! • n, for n ≥ 1 and 0!= 1 by definition, we can compute
F(n) = F(n − 1) • n with the following recursive algorithm. (ND 2015)
ALGORITHM F(n)
//Computes n! recursively
//Input: A nonnegative integer n
//Output: The value of n!
if n = 0 return 1
else return F(n − 1) * n
Algorithm analysis
For simplicity, we consider n itself as an indicator of this algorithm’s input size. i.e. 1.
The basic operation of the algorithm is multiplication, whose number of executions we
denote M(n). Since the function F(n) is computed according to the formula F(n) = F(n
−1)•n for n > 0.
The number of multiplications M(n) needed to compute it must satisfy the equality
M(n) = M(n − 1) + 1 for n >
0, M(0) = 0 for n = 0.
ALGORITHM TOH(n, A, C, B)
//Move disks from source to destination recursively
//Input: n disks and 3 pegs A, B, and C
//Output: Disks moved to destination as in the source order.
if n=1
Move nth disk directly from A to C
else
TOH(n - 1, A, B, C) // Move top n-1 disks from A to B using C
Move nth disk directly from A to C
TOH(n - 1, B, C, A) // Move top n-1 disks from B to C using A
Algorithm analysis
The number of moves M(n) depends on n only, and we get the following recurrence
equation for it:
M(n) = M(n − 1) + 1+ M(n − 1) for n > 1.
With the obvious initial condition M(1) = 1, we have the following recurrence relation for the
number of moves M(n):
M(n) = 2M(n − 1) + 1 for n > 1,
M(1) = 1.
We solve this recurrence by the same method of backward substitutions:
M(n) = 2M(n − 1) + 1 sub. M(n − 1) = 2M(n − 2) + 1
= 2[2M(n − 2) + 1]+ 1
= 22M(n − 2) + 2 + 1 sub. M(n − 2) = 2M(n − 3) + 1
2
= 2 [2M(n − 3) + 1]+ 2 + 1
= 23M(n − 3) + 22 + 2 + 1 sub. M(n − 3) = 2M(n − 4) + 1
4 3 2
= 2 M(n − 4) + 2 + 2 + 2 + 1
…
= 2iM(n − i) + 2i−1 + 2i−2 + . . . + 2 + 1= 2iM(n − i) + 2i − 1.
…
Since the initial condition is specified for n = 1, which is achieved for i = n − 1,
M(n) = 2n−1M(n − (n − 1)) + 2n−1 – 1 = 2n−1M(1) + 2n−1 − 1= 2n−1 + 2n−1 − 1= 2n − 1.
Thus, we have an exponential time algorithm
EXAMPLE 3: An investigation of a recursive version of the algorithm which finds the number of
binary digits in the binary representation of a positive decimal integer.
ALGORITHM BinRec(n)
//Input: A positive decimal integer n
//Output: The number of binary digits in n’s binary representation
The number of additions made in computing BinRec(ہn/2]) is A(ہn/2]), plus one more
Algorithm analysis
Since the recursive calls end when n is equal to 1 and there are no additions made
backward substitutions
A(2k) = A(2k−1) + 1 substitute A(2k−1) = A(2k−2) + 1
= [A(2k−2) + 1]+ 1= A(2k−2) + 2 substitute A(2k−2) = A(2k−3) + 1
= [A(2k−3) + 1]+ 2 = A(2k−3) + 3 ...
...
= A(2k−i) + i
...
= A(2k−k) + k.
Thus, we end up with A(2k) = A(1) + k = k, or, after returning to the original variable n = 2k and
hence k = log2 n,
A(n) = log2 n ϵ Θ (log2 n).
EXAMPLE 1: Consider the problem of finding the value of the largest element in a list of n
numbers. Assume that the list is implemented as an array for simplicity.
ALGORITHM MaxElement(A[0..n − 1])
//Determines the value of the largest element in a given array
//Input: An array A[0..n − 1] of real numbers
//Output: The value of the largest element in A
maxval ←A[0]
for i ←1 to n − 1 do
if A[i]>maxval
maxval←A[i]
return maxval
Algorithm analysis
The measure of an input’s size here is the number of elements in the array, i.e., n.
There are two operations in the for loop’s body:
o The comparison A[i]> maxval and
o The assignment maxval←A[i].
The comparison operation is considered as the algorithm’s basic operation, because the
comparison is executed on each repetition of the loop and not the assignment.
The number of comparisons will be the same for all arrays of size n; therefore, there is no
need to distinguish among the worst, average, and best cases here.
Let C(n) denotes the number of times this comparison is executed. The algorithm makes
one comparison on each execution of the loop, which is repeated for each value of the
loop’s variable i within the bounds 1 and n − 1, inclusive. Therefore, the sum for C(n) is
calculated as follows:
𝑛−1
(𝑛) = ∑ 1
i=1
i.e., Sum up 1 in repeated n-1 times
(𝑛) = 𝑛 − 1
EXAMPLE 2: Consider the element uniqueness problem: check whether all the Elements in a given
array of n elements are distinct.
Algorithm
analysis
The natural measure of the input’s size here is again n (the number of elements in the array).
Since the innermost loop contains a single operation (the comparison of two elements), we
should consider it as the algorithm’s basic operation.
The number of element comparisons depends not only on n but also on whether there are
equal elements in the array and, if there are, which array positions they occupy. We will
limit our investigation to the worst case only.
One comparison is made for each repetition of the innermost loop, i.e., for each value of the
loop variable j between its limits i + 1 and n − 1; this is repeated for each value of the outer
loop, i.e., for each value of the loop variable i between its limits 0 and n − 2.
where C[i, j ]= A[i, 0]B[0, j]+ . . . + A[i, k]B[k, j]+ . . . + A[i, n − 1]B[n − 1, j] for every pair of
indices 0 ≤ i, j ≤ n − 1.
return C
Algorithm analysis
An input’s size is matrix order n.
There are two arithmetical operations (multiplication and addition) in the innermost loop.
But we consider multiplication as the basic operation.
Let us set up a sum for the total number of multiplications M(n) executed by the algorithm.
Since this count depends only on the size of the input matrices, we do not have to
investigate the worst-case, average-case, and best-case efficiencies separately.
There is just one multiplication executed on each repetition of the algorithm’s innermost
loop, which is governed by the variable k ranging from the lower bound 0 to the upper
bound n − 1.
Therefore, the number of multiplications made for every pair of specific values of variables
i and j is
The total number of multiplications M(n) is expressed by the following triple sum:
Now, we can compute this sum by using formula (S1) and rule (R1)
.
The running time of the algorithm on a particular machine m, we can do it by the product
EXAMPLE 4 The following algorithm finds the number of binary digits in the binary
representation of a positive decimal integer.
ALGORITHM Binary(n)
//Input: A positive decimal integer n
//Output: The number of binary digits in n’s binary representation
count ←1
while n > 1 do
n←𝖫n/2⏌
count ←count + 1
return count
Algorithm analysis
An input’s size is n.
The loop variable takes on only a few values between its lower and upper limits.
Since the value of n is about halved on each repetition of the loop, the answer should be
about log2 n.