Adsaa Unit-2 (R23)
Adsaa Unit-2 (R23)
ALGORITHM
4. Finiteness: If we trace out the instructions of an algorithm, then for all cases, the algorithm
terminates after a finite number of steps.
5. Effectiveness: Every instruction must be very basic so that it can be carried out, in principle,
by a person using only pencil and paper. It is not enough that each operation be definite as in
criterion 3; it also must be feasible.
1. How to devise algorithms: Creating an algorithm is an art which may never be fully
automated. A major goal is to study various design techniques that have proven to be useful. By
mastering these design strategies, it will become easier for us to devise new and useful
algorithms. Dynamic programming is one such technique. Some of the techniques are especially
useful in fields other than computer science such as operations research and electrical
engineering
2. How to validate algorithms: Once an algorithm is devised, it is necessary to show that it
computes the correct answer for all possible legal inputs. We refer to this process as algorithm
validation The purpose of the validation is to assure us that this algorithm will work correctly
independently of the issues concerning the programming language it will eventually be written
in.
4. How to test a program: Means that Debugging and profiling (or performance measurement).
Debugging is the process of executing programs on sample data sets to determine whether faulty
ALGORITHM SPECIFICATION:
Pseudo-code Conventions
We can describe an algorithm using natural language like English and simple mathematical
statements called pseudo-code. We must make sure that the resulting instructions are definite.
Graphic representations called flowcharts are another possibility, but they work well only if the
algorithm is small and simple. We present most of our algorithms using a pseudo-code
1. Comments begin with ‘// ’ and continue until the end of line.
2. Blocks are indicated with matching braces: { and }. A compound statement (i.e., a collection
of simple statements) can be represented as a block. The body of a procedure also forms a block.
Statements are delimited by ; .
3. An identifier begins with a letter. The data types of variables are not explicitly declared. The
types will be clear from the context. Whether a variable is global or local to a procedure will also
be evident from the context. We assume simple data types such as integer, float, char, boolean,
and so on. Compound data types can be formed with records as follows
node record
{
datatype 1 data 1;
.
.
datatypen data_n;
node *link;
In this example, link is a pointer to the record type node. Individual data items of a record can be
accessed with and period. For instance if p points to a record of type node, p→ data 1 stands for
the value of the first field in the record. On the other hand, if q is a record of type node, q.data_1
will denote its first field.
(variable):= (expression);
5. There are two boolean values true and false. In order to produce these values, the logical
operators and, or, and not and the relational operators <, <= , =, ≠, > , >= are provided.
7. The following looping statements are employed: for, while, and repeat until. The while loop
takes the following form:
while (condition) do
(statement 1)
(statement n)
As long as (condition) is true, the statements get executed. When (condition) becomes false, the
loop is exited. The value of (condition) is evaluated at the top of the loop.
(statement 1)
(statement n)
Here valuel, value2, and step are arithmetic expressions. A variable of type integer or real or a
numerical constant is a simple form of an arithmetic expression. The clause "step step" is
optional and taken as +1 if it does not occur. step could either be positive or negative. variable is
tested for termination at the start of each iteration. The for loop can be implemented as a while
loop as follows:
variable : valuel;
fin := value2;
incr := step;
(statement n)
}
A repeat-until statement is constructed as follows:
repeat
{
(statement 1)
.
.
.
(statement n)
}until (condition);
The statements are executed as long as (condition) is false. The value of (condition) is computed
after executing the statements.The instruction break; can be used within any of the above looping
instructions to force exit. In case of nested loops, break; results in the exit of the innermost loop
that it is a part of. A return statement within any of the above also will result in exiting the loops.
A return statement results in the exit of the function itself.
Here (condition) is a boolean expression and (statement), (statement 1), and (statement 2) are
arbitrary statements (simple or compound).
case
{
:(condition 1): (statement 1)
:else: (statement n + 1)
}
9. Input and output are done using the instructions read and write. No format is used to specify
the size of input or output quantities.
10. There is only one type of procedure: Algorithm. An algorithm con-sists of a heading and a
body. The heading takes the form
where Name is the name of the procedure and ((parameter list)) is a listing of the procedure
parameters. The body has one or more (simple or compound) statements enclosed within braces
{ and }. An algorithm may or may not return any values. Simple variables to procedures are
passed by value. Arrays and records are passed by reference. An array name or a record name is
treated as a pointer to the respective data type.
Space Complexity
Space complexity of an algorithm represents the amount of memory space needed the
algorithm in its life cycle. Space needed by an algorithm is equal to the sum of the following two
components
1. A fixed part that is independent of the characteristics (e.g., number, size) of the inputs and
outputs. This part typically includes the in-struction space (i.e., space for the code), space for
simple variables and fixed-size component variables (also called aggregate), space for constants,
and so on.
2. A variable part that consists of the space needed by component variables whose size is
dependent on the particular problem instance being solved, the space needed by referenced
variables (to the extent that this depends on instance characteristics), and the recursion stack
space (in so far as this space depends on the instance characteristics).
The space requirement S(P) of any algorithm P may therefore be written as S(P) = c + SP (I)
(instance characteristics), where c is a constant. the space complexity of an algorithm, we
concentrate solely on estimating S( P) ( instance characteristics).
Example:-
1. Algorithm
abc(x,y,z)
{
return x*y*z+(x-y)
}
Here we have three variables x, y and z. x,y and z each variable requires one unit of memory.
S(p) = c + sp
S(p) = 3 + 0
S(p)=3
2. Algoritham sum(x,n)
{
Total:=0
For i←1 to n do
Total:=total + x[i]
}
S(p)=c+sp
Here c = x,n,total (each one requires one unit of memory)=3
Sp=x[i] and its depends on n
S(p)=3+n
Time complexity
The time complexity of an algorithm is defined as the amount of time taken by an algorithm to
run as a function of the length of the input. Note that the time to run is a function of the length
of the input and not the actual execution time of the machine on which the algorithm is running
on. To estimate the time complexity, we need to consider the cost of each fundamental
instruction and the number of times the instruction is executed.
1. We introduce a variable, count into the program statement to increment count with initial
value 0.Statement to increment count by the appropriate amount are introduced into the program.
This is done so that each time a statement in the original program is executes count is
incremented by the step count of that statement.
}
Count : = count+1; // for last exexution
return S;
Count : = count+1;
}
If the count is zero to start with, then it will be 2n+3 on termination. So each invocation of sum
executes a total of 2n+3 steps.
2. The second method to determine the step count of an algorithm is to build a table in which we
list the total number of steps contributes by each statement.
First determine the number of steps per execution (s/e) of the statement and the total number of
times (ie., frequency) each statement is executed. By combining these two quantities, the total
contribution of all statements, the step count for the entire algorithm is obtained.
1. Algorithm Sum(a,n) 0 - 0
2.{ 0 - 0
3. s:=0.0; 1 1 1
4. for i:=1 to n do 1 n+1 n+1
5. s:=s+a[i]; 1 n n
6. return s; 1 1 1
7.} 0 - 0
Total 2n+3
Algorithm RSum(a, n)
{
if (n <= 0) then return 0.0;
else return RSum (a, n - 1) + a[n]
}
The step count is useful in that it tells us how the run time for a program changes with changes in
the instance characteristics. From the step count of Sum, we see that if n is doubled, the run time
also doubles (approximately)
ASYMPTOTIC NOTATIONS
Asymptotic notations are used to represent the complexities of algorithms for asymptotic
analysis. These notations are mathematical tools to represent the complexities. There are three
notations that are commonly used.
Big-O Notation (O): It represents the upper bound of the runtime of an algorithm. Big O
Notation's role is to calculate the longest time an algorithm can take for its execution, i.e., it is
used for calculating the worst-case time complexity of an algorithm.
Let g and f be functions from the set of natural numbers to itself. The function f is said to be
O(g) (read big-oh of g), if there is a constant c > 0 and a natural number n0 such that
We write f(n) = Ω(g(n)), If there are positive constants n0 and c such that,
Example-1:-
f(n) = 2n+2, g(n) = n
2n ≤ 2n + 2, for all n ≥ 2, i.e c=2, n0 =2
i.e cg(n) ≤ f(n) for all n ≥ n0
f(n) = Ω(g(n)) for all n ≥ 2,
We write f(n) = Θ(g(n)), If there are positive constants n0 and c1 and c2 such that, to the right of
n0 the f(n) always lies between c1*g(n) and c2*g(n) inclusive.
Θ(g(n)) = {f(n) : There exist positive constant c1, c2 and n0 such that 0 ≤ c1 g(n) ≤ f(n) ≤ c2 g(n),
for all n ≥ n0}
Example-1:-
f(n) = 2n+2, c1g(n) = 2n, c2g(n)=4n,f(n) = Θ (g(n)) for all n ≥ 2
3n + 2 = Θ (n) as
3n + 2 >= 3n for all n >= 2 and
3n + 2 <= 4n for all n >= 2
Therefore 3n + 3 = Θ(n)
Example-2:-
10n2 + 4n + 2 = Θ(n2) ,
6x2n + n2 = Θ(2n)
10 x log(n) + 4 = Θ (log(n))
Example-3:-
3n+2 ≠ Θ (1)
3n+3 ≠ Θ (n2)
Flot of functions
Divide and Conquer is a fundamental algorithm design technique used to solve problems by
breaking them down into smaller sub-problems, solving the sub-problems independently, and
then combining their solutions to solve the original problem. It is widely applied in various
1. Divide: The problem is divided into smaller sub-problems of the same type.
2. Conquer: Solve the sub-problems recursively. If the sub-problem size is small enough,
solve it directly (base case).
3. Combine: Merge the solutions of the sub-problems to produce the solution to the original
problem.
T 1 n = 1
T(n) = n where a and b are known constants.
aT +f n n>1
b
We assume that T(1) is known and n is a power of b (i.e., n = bk )
One of the methods solving any such recurrence relation is called the substitution method. This
method repeatedly makes substitution for each occurrence of the function T in the right-hand
side until all such occurrences disappear.
Time Complexity
The complexity of the divide and conquer algorithm is calculated using the master theorem which
is as follow.
T(n) = aT(n/b) + f(n), where ,n = size of input, a = number of sub-problems in the recursion
n/b = size of each sub-problem.
All sub-problems are assumed to have the same size.
f(n) = cost of the work done outside the recursive call, which includes the cost of dividing the problem
and cost of merging the solutions
Now let’s take an example by finding the time complexity of a recursive problem
T(n) = aT(n/b) + f(n)
= 2T(n/2) + O(n)
Where, a = 2 (each time, a problem is divided into 2 sub-problems)
n/b = n/2 (size of each sub problem is half of the input)
f(n) = time taken to divide the problem and merging the sub-problems
T(n/2) = O(n log n) (To understand this, please refer to the master theorem.)
T(n) = 2T(n / 2) + n
= 21 T(n / 21) + 1 n
Hence
=2i T(1)) + in
=bn + n 𝑙𝑜𝑔2𝑛
= 2n + n 𝑙𝑜𝑔2𝑛
≈ O(n log n)
QUICK SORT
Quick sort is a sorting algorithm that uses the divide and conquers strategy. In this method
division is dynamically carried out. The three steps of quick sort are as follows:
Divide: split the array into two sub arrays that each elements in the left sub array is less than or
equal the middle element and each element in the right is greater than the middle element. The
splitting of the array into two sub arrays is based on pivot element. All the elements that are less
that are less than pivot should be in left sub array and all the elements that are more than pivot
should be in right sub array.
Conquer: Recursively sort the two sub array.
Combine: Combine all the sorted elements in a group to from a list of sorted elements.
2. Start from the element pointed by right and scan the array from right to left, comparing each
element on the way with the element pointed by the variable loc. That is, a[loc] should be less
than a[right].
(a) If that is the case, then simply continue comparing until right becomes equal to loc. Once
right = loc, it means the pivot has been placed in its correct position.
(b) However, if at any point, we have a[loc] > a[right], then interchange the two values and jump
to Step 3.
(c) Set loc = right
3. Start from the element pointed by left and scan the array from left to right, comparing each
element on the way with the element pointed by loc. That is, a[loc] should be greater than a[left].
(a) If that is the case, then simply continue comparing until left becomes equal to loc. Once left =
loc, it means the pivot has been placed in its correct position.
(b) However, if at any point, we have a[loc] < a[left], then interchange the two values and jump
to Step 2.
(c) Set loc = left.
Example Sort the elements given in the following array using quick sort algorithm Let us
consider an array A. 54, 26, 93, 17, 77, 31, 44, 56, 20
First element is considered as pivot element. l = A [0] and r = A [n]. l = l+1 until l > p and r = r - 1
until r < p, this is done until l > r
Now l = 77 and r = 44, then swap the positions of l and r i.e. swap (77, 44) then the array is
Here 54 are a partition element and fix the 54 position. In this position the left side elements of 54 are
arranged less than 54 and the right side elements of 54 are arranged greater than 54. Now we can
divide the array into two sub arrays.
Continue the same process finally we can get the sorted sub array-1 as
Then combine these two sorted arrays we get the sorted array as
Algorithm QuickSort(p, q)
// Sorts the elements a[p], ..., a[q] which reside in the global
// array a[1: n] into ascending order; a[n+1] is considered to
// be defined and must be ≥ all the elements in a[1: n].
{
if (p <q) then // If there are more than one element
{
// divide P into two sub-problems.
j := Partition(a, p, q + 1); // j is the position of the partitioning element.
// Solve the sub-problems.
QuickSort(p, j – 1);
QuickSort(j + 1, q);
// There is no need for combining solutions.
}
}
Worst-case value Cw(n):C(n) The number of element comparisons in each call of Partition p - m
+ 1 Let r be the total number of elements in all the calls to Partition level of recursion. At level
one only one call, Partition (a, 1, n + 1) is made and r = n at level two at most two calls are made
and r = n - 1 and so on. At each level of recursion, O(r) element comparisons are made by
Partition. At each level, r is at least one less than the r at the previous level as the partitioning
elements of the previous level are eliminated. Hence Cw(n) is the sum on r as r varies from 2 to n,
or O(n2) .
Average value CA(n): C(n) is much less than Cw(n) Under the assumptions made earlier
partitioning element v has an probability of being the ith-smallest element, 1 <= i <= p - m in
a[m : p - 1] Hence the two sub-arrays remaining to be sorted a[m:j] a[j + 1 : p - 1] with
probability 1 / (p - m), m <= j < p From this we obtain
CA(n) = n + 1 + 1/n 1≤𝑘≤𝑛 [CA (k − 1)) + CA (n − k) ]
MERGE SORT
Merge sort is a sorting algorithm that uses the divide, conquer, and combine algorithmic
paradigm.
Divide means partitioning the n-element array to be sorted into two sub-arrays of n/2 elements. If
A is an array containing zero or one element, then it is already sorted. However, if there are more
elements in the array, divide A into two sub-arrays, A1 and A2, each containing about half of the
elements of A.
Conquer means sorting the two sub-arrays recursively using merge sort.
Combine means merging the two sorted sub-arrays of size n/2 to produce the sorted array of n
elements.
Merge sort algorithm focuses on two main concepts to improve its performance (running time):
A smaller list takes fewer steps and thus less time to sort than a large list.
As number of steps is relatively less, thus less time is needed to create a sorted list from
two sorted lists rather than creating it using two unsorted lists.
Time Complexity:
The time for the merging operation is proportional to n. then the computing time for merge sort
is described by the recurrence relation
Consider two matrices A and B each of the size N*N. We want to calculate the resultant matrix
C which is formed by multiplying the given two matrices i.e, A and B.Matrix A has a
size N*M and matrix B has a size A*B. Given two matrices are multiplied only when the number
of columns in the first matrix is equal to the number of rows in the second matrix. Therefore, we
can say that matrix multiplication is possible only when M==A.
Where the given matrices are square matrices of size is N*N each.For multiplying every column
with every element in the row from the given matrices uses two loops, and adding up the values
takes another loop. Therefore the overall time complexity turns out to be O(N^3).
The main idea is to use the divide and conquer technique in this algorithm. We need to divide
matrix A and matrix B into 8 sub matrices and then recursively compute the submatrices of the
result.
For each multiplication of size N2×N2N2×N2, follow the below recursive function as
Algorithm MM(A,B,n)
{
if(n<=2) then
{
C11=A11B11+ A12B21;
C12=A11B12+ A12B22;
C21=A21B11+ A22B21;
C22=A21B12+ A22B22;
}
else
{
MM(A11 ,B11 ,n/2)+MM(A12 ,B21,n/2);
MM(A11 ,B121 ,n/2)+MM(A12,B22,n/2);
MM(A21 ,B11,n/2)+MM(A22 ,B21,n/2);
MM(A21 ,B11 ,n/2)+MM(A22 ,B22,n/2);
}
}
Where the resultant matrix is C and can be obtained in the following way. Now we need to store
the result as follows:
C11=A11∗B11+A12∗B21 //2 multiplications and 1 addition , input size n/2
C12=A11∗B12+A12∗B22 //2 multiplications and 1 addition, input size n/2
C21=A21∗B11+A22∗B21 //2 multiplications and 1 addition, input size n/2
C22=A21∗B12+A22∗B22 //2 multiplications and 1 addition, input size n/2
ADVACED DATA STRUCTURES & ALGORITHM ANALYSIS UNIT-2[R23] Page 25
// Total 8 multiplications and 4 additions, input size n/2
3
The recurrence relation obtained is: T(n)=8T(n/2)+4n2 ≈ O(n ).
To optimize it further, we use Strassen’s Matrix Multiplication where we don't need 8 recursive
calls, as we can solve them using 7 recursive calls and this requires some manipulation which is
achieved using addition and subtraction.
C11 = P + S – T + V
C12 = R + T
C13 = Q + S
C14= P + R – Q + U
Complexity Analysis:
We just need 7 Strassen’s Matrix Multiplications and some 12 additions and 6 subtraction has to
be performed to find the answer. Therefore we have the recurrence relation
b for n ≤ 2
T(n)= n 2
7T 2 + an for n > 2
By solving the above relation
T(n) = 7T(n/2)+an2
= 7 [ 7T(n/4) +a (n/2)2] + an2
= 72 T(n/4)+7a(n/2)2+an2
= 72 [ 7T(n/8) +7a(n/4)2] + an2
= 73T(n/8)+72a(n/4)2+7a(n/2)2+an2
=73 [ 7T(n/16) +7a(n/8)2] +72a(n/4)2+7a(n/2)2+an2
2.81
We got the overall time complexity of approximately is O(n ) which is better than O(n3).
CONVEX HULL
A convex hull is the smallest convex polygon that completely encloses a set of points in a two-
dimensional or three-dimensional space. It can be thought of as the "envelope" or "wrapper" of
the points. We use the divide and conquer approach to solve this by recursively calling the
function for smaller parameters.
Then, in the two spaces S1 and S2, we will find out the farthest point:
Algorithm Quick-hull(X)
Step 1: Identify two points p1 and p2 with smallest and largest x-coordinate values.
Algorithm Hull(XK,p1,p2)
Step 1: if Xk is null then return.
Step 2: for each p in Xk find the farthest point p3 with largest area.
Step 3: add p3 to convex hull.
Step 4: let Xk1 be all the points to the left of line segment p1, p3.
Step 5: let Xk2 be all the points to the right of line segment p2, p3.
Step 6: Hull(Xk1,p1,p3);
Step 7: Hull(Xk1,p2,p3);
Time Complexity:
Worst-case value: O(n2)
Average value/Best-case Value: O(n logn)