You are on page 1of 15

DAA TEXT BOOK NOTES 1

1) WHATIS AN ALGORITHM?

Algorithm references a method that can be used by a computer for finding the solution of a problem.
An algorithm is a finite set of instructions that, if followed, accomplishes a particular task. In addition, all
algorithms must satisfy the following criteria:
1. Input. Zero or more quantities are externally supplied.
2. Output. At least one quantity is produced.
3. Definiteness. Each instruction is clear and unambiguous.
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 criterion3;
it also must be feasible.

 According to criterion 3, each operation must be definite, meaning that it must be perfectly clear what
should be done. Directions such as add 9 or 26 to z or compute 9/0 are not permitted because it is not
clear which of the two possibilities should be done or what the result is.
 Effectiveness example – Performing arithmetic on integers is an example of an effective operation,
but arithmetic with real numbers is not, since some values may be expressible only by infinitely long
decimal expansion. Adding two such numbers would violate the effectiveness property.
Algorithms that are definite and effective are also called computational procedures.
The study of algorithms includes 4 distinct areas:
1. How to devise algorithm – By mastering in algorithms design strategies, it will become easier for
programmers to devise new and useful algorithms.

2. How to validate algorithm – 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. After
completion of algorithm validation, a program can be written and a second phase begins. This phase
is referred to as program proving or sometimes as program verification.

3. How to analyse algorithms – This field of study is called analysis of algorithms. As an algorithm is
executed, it uses the computer's central processing unit (CPU) to perform operations and its memory
(both immediate and auxiliary) to hold the program and data. Analysis of algorithms or performance
analysis refers to the task of determining how much computing time and storage an algorithm
requires. This is a challenging area which sometimes requires great mathematical skill. An important
result of this study is that two algorithms can be compared quantitatively.

4. How to test a program – Testing a program consists of two phases: debugging and profiling (or
performance measurement). Debugging is the process of executing programs on sample data sets to
determine whether faulty results occur and, if so, to correct them. However, as E.Dijkstra has pointed
out, "debugging can only point to the presence of errors, but not to their absence. "In cases in which
we cannot verify the correctness of output on sample data, the following strategy can be employed:
let more than one programmer develop programs for the same problem, and compare the outputs
produced by these programs. If the outputs match, then there is a good chance that they are correct.
Profiling or performance measurement is the process of executing a correct program on datasets and
measuring the time and space it takes to compute the results
DAA TEXT BOOK NOTES 2
ALGORITHM SPECIFICATION
Pseudocode Convention
In computational theory, we distinguish between an algorithm and a program. The latter does not have to
satisfy the finiteness condition. For example, we can think of an operating system that continues in a loop
until more jobs are entered. Such a program does not terminate unless the system crashes.
We can describe an algorithm in many ways. We can use a natural language like English, although if we
select this option, 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.
 Algorithms are presented using a pseudocode consists of the steps as

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 datatypes such as integer, float, char, boolean, and soon.
Compound data types can be formed with records. Here is an example:
node = record
{ datatype_1 data_1;
.
.
.
datatype_n 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.

4. Assignment of values to variables is done using the assignment statement


〈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 <, ≤, =, ≠, ≥ and > are provided.

6. Elements of multidimensional arrays are accessed using [ and ]. For example, if A is a two
dimensional array, the (i, j)th element of the array is denoted as - A[i, j]. Array indices start at zero.

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〉
DAA TEXT BOOK NOTES 3
}
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. The general form of a for loop
is
for variable := valuel to value2 step step do
{ 〈statement1〉
.
.
.
〈statementn〉
}
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;
while ((variable - fin) * step ≤ 0) do
{
〈statement 1〉
.
.
.
〈statement n〉
variable := variable + incr;
}
A repeat-until statement is constructed as follows:
repeat
〈statement 1〉
.
.
.
〈statement n〉

until 〈condition〉
Statements are executed as long as the condition is false. Also, we can use break and return statements.
8. A conditional statement has the following forms:
if 〈condition〉 then 〈statement〉
if 〈condition) then〈statement1〉 else 〈statement2〉

case
{
:〈condition 1〉: 〈sttement 1〉
.
.
.
:〈condition n〉: 〈sttement n〉
:else: 〈sttement n + 1〉
}
DAA TEXT BOOK NOTES 4

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 consists of a heading and a body. The
heading takes the form Algorithm Name(〈parameter list〉) 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 datatype.
The following algorithm finds and then returns maximum value form a given set of n values.
1 Algorithm Max(A, n)
2 // A is array of size n
3 {
4 Result := A[0];
5 for i := 2 to n do
6 if A[i] > Result then
7 Result := A[i];
8 return Result;
9 }
Name of algorithm is Max, A and n are procedure parameters. Result and i are local variables.

Selection Sort Algorithm Pseudocode

1 Algorithm SelectionSort(A, n)
2 sort the array A[1: n] into nondecreasing order
3 {
4 for i := 1 to n do
5 {
6 j := i;
7 for k := i + 1 to n do
8 if (A[k] < A[j] ) then
9 j := k;
10 t := A[i];
11 A[i] := A[j];
12 A[j] := t;
13 }
14 }

PERFORMANCE ANALYSIS OF ALGORITHMS


There are many criteria upon which we can judge an algorithm. For instance:
1. Does it do what we want it to do?
2. Does it work correctly according to the original specifications of the task?
3. Is there documentation that describes how to use it and how it works?
4. Are procedures created in such a way that they perform logical sub-functions?
5. Is the code readable?

 For larger software systems above criteria are very important and compulsory to follow. While
writing programs, programmers must achieve the above goals.
DAA TEXT BOOK NOTES 5
 Computing time and storage requirements are other criteria for judging algorithms that have a more
direct relationship to performance

Algorithm space complexity


The space complexity of an algorithm is the amount of memory it needs to run to completion. The space
needed by an algorithm is the sum of the following components:

1) Fixed space which is independent of the characteristics (e.g., number and size) of the inputs and
outputs) this part typically includes the instruction space (space for the code), space for the simple
variables and fixed size component variables, 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.

The space requirement S(P) of any algorithm P may therefore be written as S(P) = c + S P(instance
characteristics) where c is a constant. Note that when analysing the space complexity of an algorithm,
we completely (solely) estimate only S P(instance characteristics). Instance characteristics are problem
specific. Number of input and output variables and the size of each input and output variable are the
important factors for finding space complexity of an algorithm.

Calculate the time complexity of the following algorithm:


-----------------------------------------------------------------------------------------------------
1 Algorithm abc(a, b, c)
2 {
3 return a + b + b*c + (a + b – c)/(a + b) + 9.0;
4 }
------------------------------------------------------------------------------------------------------
Algorithm_1 computes return a + b + b*c + (a + b – c)/(a + b) + 9.0;
Time complexity of Algorithm_1 = S P(instance characteristics) = 0. Because there are component
variables, referenced variables and recursion stack space.

Calculate the time complexity of the following algorithm:


-----------------------------------------------------------------------------------------------------
1 Algorithm sum(a, n)
2 {
3 s := 0.0;
4 for i := 1 to n do
5 s := s + a[i];
6 return s;
7 }
------------------------------------------------------------------------------------------------------
Algorithm_2 iterative function for finding sum of all the elements in the array, a.
Space needed for the integer variable n = 1 word
Space needed for the integer variable i = 1 word
Space needed for the float variable s = ≥1 word
Space needed for the array variable a = ≥ n words
Therefore space complexity of an algorithm = SSum(n) ≥ (n + 3)
DAA TEXT BOOK NOTES 6
Calculate the time complexity of the following algorithm:
--------------------------------------------------------------------------------------------------------------------
1 Algorithm RSum(a, n)
2 {
3 if(n ≤ 0) then
4 return 0.0;
5 else
6 return RSum(a, n – 1) + a[n];
7 }
--------------------------------------------------------------------------------------------------------------------
Algorithm_3 recursive function for finding sum of all the elements in the array, a.
The recursion stack space includes space for the formal parameters, the local variables, and the return
address. Assume that the return address requires only one word of memory. Each call to RSum requires
at least three words (including space for the values of n, the return address, and a pointer to a[]). Since
the depth of recursion is n + 1, the recursion stack space needed is ≥ 3(n + 1).
That is RSum(n) ≥ 3(n + 1).
---------------------------------------------------------------------------------------------------------------------
Algorithm time complexity
The time complexity of an algorithm is the amount of computer time it needs to run to completion.
Performance evaluation of an algorithm is divided into 2 steps:
1) a priori estimates and
2) a posteriori testing
These two steps are called performance analysis and performance measurement respectively of the
algorithm.

Time complexity of a program P is denoted by T(P) is the sum of the compile time and the run time. The
compile time does not depend on the instance characteristics and only run time is important. This run
time is denoted by tP(instance characteristics)

There are two ways for finding the number of steps needed by a program to solve a particular problem
instance. In the first method a global variable with initial value 0 is used. Each time a statement in the
original program is executed, count is incremented.

Calculate the time complexity of the following algorithm using step count first method
-----------------------------------------------------------------------------------------------------
1 Algorithm sum(a, n)
2 {
3 s := 0.0;
4 count := count + 1; //count is a global variable it is initialized to zero
5 for i := 1 to n do //
6 {
7 count := count + 1; // For for
8 s := s + a[i]; count := count + 1; // For assignment
9 }
10 count := count + 1; // For the last time of for
11 count := count + 1; // For the return
12 return s;
13 }
DAA TEXT BOOK NOTES 7
------------------------------------------------------------------------------------------------------
Step table method is the second way of finding step count of the program. The number of steps per
execution (s/e) of the statement and the total number of times (i.e., frequency) each statement is
executed. The s/e of a statement is the amount by which the count changes as a result of the execution of
that statement. By combining these two quantities, the total contribution of each statement is obtained.
By adding the contributions of all statements, the step count for the entire algorithm is obtained.

Statement s/e frequency Total steps


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
Table-1 Step table for algorithm_2

Statement s/e frequency Total steps


n=0 n>0 n=0 n>0
1 Algorithm RSum(a, n) 0 – – 0 0
2 {
3 if(n ≤ 0) then 1 1 1 1 1
4 return 0.0; 1 1 0 1 0
5 else
6 return RSum(a, n – 1) + a[n]; 1+x 0 1 0 1+x
7 } 0 – – 0 0
Total 2 2+x
x = tRSum(n – 1)
Table-2 Step table for Algorithm_3 recursive sum

Statement s/e frequency Total steps


1 Algorithm Add(a, b, c, m, n) 0 – 0
2 { 0 – 0
3 for i := 1 to m do 1 m+1 m +1
4 for j := 1 to n do 1 m(n + 1) mn + m
5 c[i, j] := a[i, j] + b[i, j]; 1 mn mn
6 } 0 – 0
Total 2mn + 2m + 1
Table_3 Step table for Matrix addition

6) What are asymptotic notations? Explain


There are three types of notations in which the complexity can be represented:
1) Big Oh notation(O):It is used to indicate the upper bound or the worst case time complexity of an
algorithm.
2) Big Theta notation(θ): It is used to indicate the complexity for an algorithm averaged over all
possible inputs.
3) Big Omega notation(Ω): It is used to indicate the lower bound or the best case time complexity of
an algorithm.
DAA TEXT BOOK NOTES 8
We will use asymptotic notation primarily to describe the running times of algorithms. Asymptotic
notations are used to write fastest and slowest possible running time for an algorithm. These are also
referred to as 'best case' and 'worst case' scenarios respectively. Asymptotic notation is a way to describe
the running time complexity of an algorithm based on the input size. Asymptotic notation describes how
an algorithm performs as the size of the input grows.

O-notation: The Θ-notation asymptotically bounds a function from above and below. When we have only
an asymptotic upper bound, we use O-notation. When we have only an asymptotic lower bound, we use
Ω-notation. Since O-notation describes an upper bound, when we use it to bound the worst-case running
time of an algorithm.

What is an Amortized Algorithm Analysis?


In an amortized analysis, we average the time required to perform a sequence of data-structure operations
over all the operations performed. With amortized analysis, we can show that the average cost of an
operation is small, if we average over a sequence of operations, even though a single operation within the
sequence might be expensive.

1) Aggregate analysis determines the upper bound T(n) on the total cost of a sequence of n operations,
then calculates the amortized cost to be T(n) / n.
2) Amortized time complexity in data structures analysis will give the average time taken per operation
3) Amortized analysis is useful for designing efficient algorithms for data structures such as dynamic
arrays, priority queues, and disjoint-set data structures.
4) The amortized analysis doesn’t involve probability.

What is an Average Algorithm Analysis?


DAA TEXT BOOK NOTES 9
1) Average case analysis relies on probabilistic assumptions about the data structures and operations
in order to compute an expected running time of an algorithm.
2) In the aggregate method, we simply compute the worst-case sequence complexity of a sequence of
operations and divide by the number of operations in the sequence.
3) In this method, if n operations take worst-case time T(n) in total. Then the amortized cost of each
operation is T(n)/n.
4) Amortized cost in aggregate analysis is defined to be average cost.
Time complexity of merge sort using recursion tree method

To compute the total cost represented by the recurrence costs of all the levels are added. The recurrence
tree contains (log n + 1) levels and the cost of each level is cn. So, total cost of the tree = cn * (log n + 1)
= cn log n + cn. = O(n log n).

Next, we add the costs across each level of the tree. The top level has total cost cn, the next level down
has total cost c(n/2) + c(n/2) = cn. The level after that has total cost c(n/4) + c(n/4) + c(n/4) + c(n/4) = cn,
and so on. In general, the level i below the top has 2 i nodes, each contributing a cost of c(n/2i), so that the
ith level below the top has total cost 2 i * c(n/2i) = cn. The bottom level has n nodes, each contributing a
cost of c, for a total cost of cn. The total number of levels of the recursion tree is log n + 1, where n is the
number of leaves, corresponding to the input size.

Find the solution for the recurrence problem


When n is an exact power of 2, the solution of the recurrence

{
2if n=2
T ( n )=
2T ()
n
2
k
+ nif n=2 , for k >1

is T(n) = (n log n)
The master theorem
Let a ≥ 1 and b > 1 be constants, let f(n) be a function, and let T(n) be defined on the nonnegative
integers by the recurrence
DAA TEXT BOOK NOTES 10
T(n) = aT(n/b) + f(n)
Where we interpret n/b to mean either ⌊ n/b ⌋ or ⌈ n/b ⌉ . Then T(n) has the following asymptotic bounds:

1) if f ( n )=O ( n log ) for some constant ε >0 , thenT ( n )=Θ ( n log )


a−ε a
b b

2) if f ( n )=Θ ( n log ) for some constant ε > 0 ,then T ( n ) =Θ ( nlog log n )


a a
b b

3)
if f ( n )=Ω ( n ) for some constant ε >0 ,∧if a f ( nb ) ≤ c f ( n) for some constant c <1∧all sufficiently large n ,then T ( n)=Θ
a+ ε
log b

 To use the master method, we simply determine which case (if any) of the master theorem applies
and write down the answer.

Problem-1) Use master method for finding the solution of T(n) = 9T(n/3) + n
For this recurrence, we have a = 9, b = 3, f(n) = n and we have n log =n log =Θ ( n2 )
a 9
b 3

Since f ( n )=O ( n log ) , where ε=1


9−ε
3

Apply case-1 of the master theorem and the conclusion is that T(n) = Θ ( n2 )

Problem-2) Use master method for finding the solution of T(n) = T(2n/3) + 1
a 1

Here, a = 1 and b = 3/2, f(n) = = 1 and n log =nlog = n0 = 1 Master method Case-2 is applicable, because
b 3/2

f ( n )=Θ ( n log ) =Θ (1 ) . Solution to the recurrence is T(n) = Θ ¿


a
b

-----------------------------------------------------------------------------------------------------------------
Master Theorem is used to determine running time of algorithms (divide and conquer algorithms) in
terms of asymptotic notations. Consider a problem that is solved using recursion.

According to master theorem the runtime of the recursive algorithm can be expressed as:
T ( n )=aT
n
b
+ f (n) ()
where n = size of the problem
a = number of sub-problems in the recursion and a >= 1
n/b = size of each sub-problem
f(n) = cost of work done outside the recursive calls like dividing into sub-problems and cost of
combining them to get the solution.
Not all recurrence relations can be solved with the use of the master theorem i.e. if
T ( n ) is not monotone , ex :T ( n )=sin ⁡(n)

f ( n ) is not a polynomial ,ex :T ( n )=2 T ( n2 )+2 n

An advanced version of master theorem can be used to determine running time of


divide and conquer algorithms if the recurrence is of the following form:-
T ( n )=aT () n
b
+ ϴ(nk log p n)

where n = size of the problem


a = number of sub-problems in the recursion and a >= 1
DAA TEXT BOOK NOTES 11
n/b = size of each sub-problem
b > 1, k >= 0 and p is a real number.
Then,

1 ¿ if a> bk ,then T ( n )=ϴ(n loga )


k
2 ¿ if a=b , then
a

( a ) if p>−1, thenT ( n )=ϴ(nlog b log p+1 n)


a

( b ) if p=−1 ,then T ( n ) =ϴ(nlog b log ⁡( log n))


a

( c ) if p<−1 , thenT ( n )=ϴ(n logb )

k
3 ¿ ifa<b , then
( a ) if p ≥ 0 , thenT ( n )=ϴ(n k log p n)
( b ) if p< 0 ,then T ( n ) =ϴ(nk )

Advanced Master theorem for divide and conquer recurrences-


Example-1:Binary search T(n) = T(n/2) + O(1)
a = 1, b = 2, k = 0 and p = 0
bk = 1. So, a = bk and p > -1 [case 2.(a)]
a 1

T ( n )=ϴ(n logb log p+1 n) = ϴ(n log2 log 0 +1 n) = ϴ(n log n) = ϴ(log ⁡n)
0 1
DAA TEXT BOOK NOTES 12

DAA LAB PROGRAMS

// QUICK SORT PROGRAM USING RECURION


package quicksort;
DAA TEXT BOOK NOTES 13
import java.io.*;

public class Quicksort


{
public static int partition(int a[], int m, int p)
{
int v = a[m]; int i = m; int j = p;
do
{
do
{
i++;
}while(a[i] <= v);

do
{
j--;
}while(a[j] > v);

if(i < j)
{
int x = a[i];
a[i] = a[j];
a[j] = x;
}
}while(i <= j);
a[m] = a[j]; a[j] = v;
return j;
}

public static void Recursive_Quicksort(int a[], int p, int q)


{
if(p < q)
{
int j = partition(a, p, q + 1);
Recursive_Quicksort(a,p, j - 1);
Recursive_Quicksort(a,j + 1, q);
}
}

public static void printArray(int a[], int n)


{
for(int i = 0; i < n; i++)
System.out.print(" " + a[i]);
}

public static void main(String args[])


{
int n = 500;
int a[] = new int [n + 1];
a[n] = 32789;
for(int i = 0; i < n; i++)
a[i] = (int)(99.0 * Math.random());
DAA TEXT BOOK NOTES 14
System.out.println("VALUES BEFORE SORTING \n ");
//printArray(a, n);
double time1 = java.lang.System.currentTimeMillis();
Recursive_Quicksort(a,0, n);
double time2 = java.lang.System.currentTimeMillis();
double time3 = time2 - time1;
System.out.println("QUICK SORT EXECUTION TIME = " + time3);
System.out.println("\n VALUES AFTER SORTING \n ");
//printArray(a, n);
}
}

MERGE SORT RECURSIVE


// MERGE SORT RECURSION
package mergesort;
import java.io.*;
public class MergeSort
{
public static final int N = 59000;
static int b[] = new int [N];
public static void merge(int a[], int low, int mid, int high)
{
int h, i, j;
h = i = low;
j = mid + 1;

while((h <= mid) && (j <= high))


{
if(a[h] <= a[j] )
{
b[i] = a[h]; h++;
}
else
{
b[i] = a[j]; j++;
}
i++;
}
if(h > mid)
{
for(int k = j; k <= high; k++)
{
b[i] = a[k]; i++;
}
}
else
{
for(int k = h; k <= mid; k++)
{
b[i] = a[k]; i++;
}
}
DAA TEXT BOOK NOTES 15
for(int k = low; k <= high; k++)
a[k] = b[k];
}

public static void Recursive_MergeSort(int a[], int low, int high)


{
if(low < high)
{
int mid = (int)((low + high)/2.0);
Recursive_MergeSort(a, low, mid);
Recursive_MergeSort(a, mid + 1, high);
merge(a, low, mid, high);
}
}

public static void printArray(int a[], int n)


{
for(int i = 0; i < n; i++)
System.out.print(" " + a[i]);
}

public static void main(String args[])


{
int a[] = new int [N];
for(int i = 0; i < N; i++)
a[i] = (int)(99.0 * Math.random());
System.out.println("VALUES BEFORE SORTING \n ");
//printArray(a, N);
double time1 = java.lang.System.currentTimeMillis();
Recursive_MergeSort(a, 0, N - 1);
double time2 = java.lang.System.currentTimeMillis();
double time3 = time2 - time1;
System.out.println("\n MERGE SORT EXECUTION TIME = " + time3);
System.out.println("\n VALUES AFTER SORTING \n ");
//printArray(a, N);
}
}

You might also like