BCS401 ADA m1 Notes
BCS401 ADA m1 Notes
BCS401
Module-1: Introduction
Contents
1. Introduction
1.1. What is an Algorithm?
1.2. Fundamentals of Algorithmic Problem Solving
2. Fundamentals of the Analysis of Algorithm Efficiency
2.1. Analysis Framework
2.2. Asymptotic Notations
Big-Oh notation
Omega notation
Theta notation
Little-oh notation
2.3 Basic asymptotic efficiency Classes
2.4 Mathematical analysis of non-recursive algorithm
2.5 Mathematical analysis of Recursive algorithm
Module-1: Introduction
1. Introduction
1.1. What is an Algorithm?
Algorithm: An algorithm is a finite sequence of unambiguous instructions for
solving a particular problem, i.e., for obtaining a required output for any
legitimate input in a finite amount of time
Problem
Algorithm
EUCLID’S ALGORITHM:
The greatest common divisor of two nonnegative, not-both-zero
integers m and n, 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 of
Alexandria (third century B.c.) outlined an algorithm for solving this
problem in one of the volumes of his Elements most famous for its
systematic exposition of geometry. In modern terms, Euclid’s algorithm is
based on applying repeatedly the equality
gcd(m, n) = gcd(n, m mod n),
(If you are not impressed by this algorithm, try finding the greatest common
divisor of larger numbers, such as those in Problem 6 in this section’s
exercises.)
Here is a more structured description of this algorithm:
Euclid’s algorithm for computing gcd(m, n)
Step 1=If n 0, return the value of m as the answer and stop;
otherwise, proceed to Step 2.
Step 2 Divide m by n and assign the value of the remainder to r.
Step 3 Assign the value of n to m and the value of r to n. Go to Step
1. Alternatively, we can express the same algorithm in pseudocode:
ALGORITHM Euclid(m, n)
//Computes gcd(m, n) by Euclid’s algorithm
//Input: Two nonnegative, not-both-zero integers m and n
//Output: Greatest common divisor of m and n
while n /= 0 do
r › m mod n
m› n
n› r
return m
Designing an Algorithm and Data Structures - One should pay close attention
choosing data structures appropriate for the operations performed by the algorithm.
For example, the sieve of Eratosthenes would run longer if we used a linked list
instead of an array in its implementation. Algorithms + Data Structures =
Programs
Methods of Specifying an Algorithm- Once you have designed an algorithm;
you need to specify it in some fashion. These are the two options that are most
widely used nowadays for specifying algorithms. Using a natural language has
an obvious appeal;
however, the inherent ambiguity of any natural language makes a concise
and clear description of algorithms surprisingly difficult. Pseudocode is a
mixture of a natural language and programming language like constructs.
Pseudocode is usually more precise than natural language, and its usage often
yields more succinct algorithm descriptions.
Proving an Algorithm’s Correctness - Once an algorithm has been
specified, you have to prove its correctness. That is, you have to prove that the
algorithm yields a required result for every legitimate input in a finite amount
of time. For some algorithms, a proof of correctness is quite easy; for others, it
can be quite complex. 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.
Analyzing an Algorithm - After correctness, by far the most important is
efficiency. In fact, there are two kinds of algorithm efficiency: time efficiency,
indicating how fast the algorithm runs, and space efficiency, indicating how
much extra memory it uses. Another desirable characteristic of an algorithm is
simplicity. Unlike efficiency, which can be precisely defined and investigated
with mathematical rigor, simplicity, like beauty, is to a considerable degree in
the eye of the beholder.
Coding an Algorithm - Most algorithms are destined to be ultimately
implemented as computer programs. Implementing an algorithm correctly is
necessary but not sufficient: you would not like to diminish your algorithm’s
power by an inefficient implementation. Modern compilers do provide a
certain safety net in this regard, especially when they are used in their code
optimization mode.
𝑇(𝑛) ≈ 𝑐𝑜𝑝𝐶(𝑛)
implementing this algorithm on that computer by the formula:
Unless n is extremely large or very small, the formula can give a reasonable estimate
of the algorithm's running time.
It is for these reasons that the efficiency analysis framework ignores multiplicative
constants and concentrates on the count's order of growth to within a
constant multiple for large-size inputs.
Orders of Growth
Why this emphasis on the count's order of growth for large input sizes? Because for
large values of n, it is the function's order of growth that counts: just look at table
which contains values of a few functions particularly important for analysis of
algorithms.
Table: Values of several functions important for analysis of algorithms
Algorithms that require an exponential number of operations are practical for solving
only problems of very small sizes.
Performance Analysis
There are two kinds of efficiency: time efficiency and space efficiency.
● Time efficiency indicates how fast an algorithm in question runs;
● Space efficiency deals with the extra space the algorithm requires.
In the early days of electronic computing, both resources time and space were at a
premium. The research experience has shown that for most problems, we can achieve
much more spectacular progress in speed than inspace. Therefore, we primarily
concentrate on time efficiency.
Space complexity
Total amount of computer memory required by an algorithm to complete its execution
is called as space complexity of that algorithm. The Space required by an algorithm is
the sum of following components
● A fixed part that is independent of the input and output. This includes
memory space for codes, variables, constants and so on.
Time complexity
Usually, the execution time or run-time of the program is refereed as its time
complexity denoted by tp(instance characteristics). This is the sum of the time taken
to execute all instructions in the program. Exact estimation runtime is a complex
task, as the number of instructions executed is dependent on the input data. Also
different instructions will take different time to execute. So for the estimation of the
time complexity we count only the number of program steps. We can determine
the steps needed by a program to solve a particular problem instance in two ways.
Method-1: We introduce a new variable count to the program which is initialized to
zero. We also introduce statements to increment count by an appropriate amount
into the program. So when each time original program executes, the count also
incremented by the step count.
Example: Consider the algori hm sum(). After the introduction of the count the
program will be as follows. We can estimate that invocation of sum() executes total
number of 2n+3 steps.
The above method is both excessively difficult and, usually unnecessary. The thing to do is
to identify the most important operation contributing the m operation of the algorithm,
called the basic operation, the st to the total running time, and compute the number of
times the basic operation is executed.
Trade-off
There is often a time-space-tradeoff involved in a problem, that is, it cannot be
solved with few computing time and low memory consumption. One has to make a
compromise and to exchange computing time for memory consumption or vice
versa, depending on which algorithm one chooses and how one parameterizes it.
2.2. Asymptotic Notations
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: O(big oh), Ω(big
omega), Θ (big theta) and o(little oh)
Big-Oh notation
Definition: A function t(n) is said
Informally, O(g(n)) is the set of all functions with a lower or same order of
growth as g(n). Note that the definition gives us a lot of freedom in choosing
specific valuesfor constants c and n0.
Strategies to prove Big-O: Sometimes the easiest way to prove that f (n) =
O(g(n)) is to take c to be the sum of the positive coefficients off(n). We can
usually ignore the negative coefficients.
Omega notation
Definition: A function t(n) is said
Example:
Theta notation
Example: n2 + 5n + 7 = Θ(n2)
1
ruksana banu [Assistant Professor CS&E]
0
ADA BCS401
1
ruksana banu [Assistant Professor CS&E]
1
ADA BCS401
1
ruksana banu [Assistant Professor CS&E]
2
ADA BCS401
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 aboutfour
arbitrary real numbers a1, b1, a2, b2: if a1 ≤ b1 and a2 ≤ b2, then a1 + a2 ≤ 2 max{b1,
b2}.
Since t1(n) ∈O(g1(n)), there exist some positive constant c1 and some nonnegative
integer n1 such that t1(n) ≤ c1g1(n) for all n ≥ n1.
Similarly, since t2(n) ∈O(g2(n)), t2(n) ≤ c2g2(n) for all n ≥ n2.
Let us denote c3 = max{c1, c2} and consider n>=max{n1,n2} so that
we can use both inequalities. Adding them yields the following: t1(n) + t2(n) ≤
c1g1(n) + c2g2(n)
≤ c3 g1(n) + c3g2(n) = c3[g1(n) + g2(n)]
lim ƒ(𝑛) = 0
𝑛→∞
Example: 𝑔(𝑛)
1
ruksana banu [Assistant Professor CS&E]
3
ADA BCS401
1
ruksana banu [Assistant Professor CS&E]
4
ADA BCS401
Here comparison is the basic operation. Note that number of comparisions will
be same for all arrays of size n. Therefore, no need to distinguish worst, best and
average cases. Total number of basic operations are,
Example-2: To check whether all the elements in the given array are distinct
1
ruksana banu [Assistant Professor CS&E]
5
ADA BCS401
Other than the worst case, the total comparisons are less than . For example if
the first two elements of the array are equal, only one comparison is computed. So in
general C(n) =O(n2)
1
ruksana banu [Assistant Professor CS&E]
6
ADA BCS401
Example-1
1
ruksana banu [Assistant Professor CS&E]
7
ADA BCS401
….
Example-2: Tower of Hanoi puzzle. In this puzzle, There are n disks of different
sizes that canslide onto any of three pegs. Initially, all the disks are on the first peg in
order ofsize, the largest on the bottom and the smallest on top. The goal is to move all
thedisks to the third peg, using the second one as an auxiliary, if necessary. We
canmove only one disk at a time, and it is forbidden to place a larger disk on top of
asmaller one.The problem has an elegant recursive solution, which is illustrated in
Figure.
1. If n = 1, we move the single disk directly from the source peg to the destination
peg.
2. To move n>1 disks from peg 1 to peg 3 (with peg 2 as auxiliary),
o we first move recursively n-1 disks from peg 1 to peg 2 (with peg 3 as
auxiliary),
o then move the largest disk directly from peg 1 to peg 3, and,
o finally, move recursively n-1 disks from peg 2 to peg 3 (using peg 1 as
auxiliary).
1
ruksana banu [Assistant Professor CS&E]
8
ADA BCS401
1
ruksana banu [Assistant Professor CS&E]
9
ADA BCS401
We have the following recurrence relation for the number of moves M(n):
The pattern of the first three sums on the left suggests that the next one will be
2
ruksana banu [Assistant Professor CS&E]
0
ADA BCS401
2
ruksana banu [Assistant Professor CS&E]
1
ADA BCS401
CSE, SVIT 22
2.6. Brute force design technique:
Brute force is straight forward approach to solving a problem, usually
directly based on the problem statement and definitions of the concepts
involved.
• Selection sort
We start selection sort by scanning the entire given list to find its smallest element
and exchange it with the first element, putting the smallest element in its final position in the
sorted list. Then we scan the list, starting with the second element, putting the second
smallest element in its final position. Generally, on the ith pass through the list, which we
number from 0 to n-2, the algorithm searches for the last n-1 elements and swaps it with Ai:
The number of times the algorithm executed depends only on the array’s size and is
given by
• Sequential search
This is also called as Linear search. Here we start from the initial element of the array and
compare it with the search key. We repeat the same with all the elements of the array till we
CSE, SVIT 23
encounter the search key or till we reach end of the array.
CSE, SVIT 24
The time efficiency in worst case is O(n), where n is the number of elements of the array. In
best case it is O(1), it means the very first element is the search key.
Another example of Brute force approach is string matching, where string of n characters
called text and a string of m characters (m<=n) called the pattern is given. Here job is to find
whether the pattern is present in text or not. If we want to find i-the index of the leftmost
character of the first matching substring in the
We start matching with the very first character, if a match then only j is incremented and
again compared with next character of both the strings. If not then I is incremented and j starts
from beginning of pattern string. If pattern found we return the position from where the
pattern began. Pattern is tried to match till n-m elements, later we need not try to match as the
elements will be lesser than pattern. If it doesn’t match by n-m elements then pattern is not
matched.