DAA NOTES UNIT 1 (Design and Analysis of Algorithm)
DAA NOTES UNIT 1 (Design and Analysis of Algorithm)
Definition
The word algorithm comes from the name of a Persian author, Abu Ja'far Mohammed ibn Musa
al Khowarizmi ( 825 A.D.) who wrote a textbook on mathematics.
Dictionary defines algorithm meaning as "any special method of solving a certain kind of problem."
taken on a special significance in computer science, where algorithm has come to refer to a precise
method useable by a computer for the solution of a problem. This is what makes the notion of an
algorithm different from words such as process, technique or method.
An algorithm is any well-defined computational procedure that takes some value, or set of
values, as input and produces some value, or set of values, as output in a finite amount of time.
An algorithm for a computational problem is correct if, for every problem instance provided as
input, it halts finishes its computing in finite time and outputs the correct solution to the problem
instance. A correct algorithm solves the given computational problem. An incorrect algorithm might not
halt at all on some input instances, or it might halt with an incorrect answer.
Mathematics: Algorithms are used to solve mathematical problems, such as finding the optimal
solution to a system of linear equations or finding the shortest path in a graph.
Operations Research: Algorithms are used to optimize and make decisions in fields such as
transportation, logistics, and resource allocation.
Artificial Intelligence: Algorithms are the foundation of artificial intelligence and machine learning,
and are used to develop intelligent systems that can perform tasks such as image recognition,
natural language processing, and decision-making.
Data Science: Algorithms are used to analyse, process, and extract insights from large amounts of
data in fields such as marketing, finance, and healthcare.
The internet enables people all around the world to quickly access and retrieve large amounts of
information. With the aid of clever algorithms, sites on the internet are able to manage and
manipulate this large volume of data.
Electronic commerce enables goods and services to be negotiated and exchanged electronically, and
it depends on the privacy of personal information such as credit card numbers, passwords, and bank
statements.
Manufacturing and other commercial enterprises often need to allocate scarce resources in the most
beneficial way. An oil company might wish to know where to place its wells in order to maximize
its expected profit.
You have a road map on which the distance between each pair of adjacent intersections is marked,
and you wish to determine the shortest route from one intersection to another.
Given a mechanical design in terms of a library of parts, where each part may include instances of
other parts, list the parts in order so that each part appears before any part that uses it.
A doctor needs to determine whether an image represents a cancerous tumor or a benign one. The
doctor has available images of many other tumors, some of which are known to be cancerous and
some of which are known to be benign.
Properties of Algorithms
An algorithm is composed of a finite set of steps, each of which may require one or more operations.
A computer carrying out these operations necessitates that certain constraints be placed on the type of
operations an algorithm can include, each operation must be definite, meaning that it must be perfectly
clear what should be done. Another important property each operation should have is that it be effective;
each step must be such that it can, at least in principle, be done by in a finite amount of time. An
algorithm produces one or more outputs and may have zero or more inputs which are externally
supplied. Another important criterion we will assume about algorithms that they terminate after a finite
number of operations.
Characteristics of an Algorithm
An algorithm must have the following characteristics:
Clear and Unambiguous: The algorithm should be unambiguous. Each of its steps should be clear
in all aspects and must lead to only one meaning.
Well-Defined Inputs: If an algorithm says to take inputs, it should be well-defined inputs. It may
or may not take input.
Well-Defined Outputs: The algorithm must clearly define what output will be yielded and it
should be well-defined as well. It should produce at least 1 output.
Finite-ness: The algorithm must be finite, i.e. it should terminate after a finite time.
Feasible: The algorithm must be simple, generic, and practical, such that it can be executed with
the available resources. It must not contain some future technology or anything.
Language Independent: The Algorithm designed must be language-independent, i.e. it must be
just plain instructions that can be implemented in any language, and yet the output will be the same,
as expected.
Input: An algorithm has zero or more inputs. Each that contains a fundamental operator must
accept zero or more inputs.
Output: An algorithm produces at least one output. Every instruction that contains a fundamental
operator must accept zero or more inputs.
Definiteness: All instructions in an algorithm must be unambiguous, precise, and easy to interpret.
By referring to any of the instructions in an algorithm one can clearly understand what is to be
done. Every fundamental operator in instruction must be defined without any ambiguity.
Finiteness: An algorithm must terminate after a finite number of steps in all test cases. Every
instruction which contains a fundamental operator must be terminated within a finite amount of
time. Infinite loops or recursive functions without base conditions do not possess finiteness.
Effectiveness: An algorithm must be developed by using very basic, simple, and feasible operations
so that one can trace it out by using just paper and pencil.
Expressing Algorithm
Algorithm Specification: Algorithm can be described in three ways.
1. Natural language like English: implement a natural language like English, we should ensure that
each & every statement is definite.
2. Graphic representation called flowchart: This method will work well when the algorithm is
small& simple.
3. Pseudo-code Method: In this method, we should typically describe algorithms as program, which
resembles language like Pascal & algol.
Types of Algorithms:
1. Brute Force Algorithm: It is the simplest approach to a problem. A brute force algorithm is the first
approach that comes to finding when we see a problem.
2. Recursive Algorithm: A recursive algorithm is based on recursion. In this case, a problem is broken
into several sub-parts and called the same function again and again.
3. Backtracking Algorithm: The backtracking algorithm builds the solution by searching among all
possible solutions. Using this algorithm, we keep on building the solution following criteria. Whenever
a solution fails we trace back to the failure point build on the next solution and continue this process till
we find the solution or all possible solutions are looked after.
4. Searching Algorithm: Searching algorithms are the ones that are used for searching elements or
groups of elements from a particular data structure. They can be of different types based on their
approach or the data structure in which the element should be found.
5. Sorting Algorithm: Sorting is arranging a group of data in a particular manner according to the
requirement. The algorithms which help in performing this function are called sorting algorithms.
Generally sorting algorithms are used to sort groups of data in an increasing or decreasing manner.
6. Hashing Algorithm: Hashing algorithms work similarly to the searching algorithm. But they contain
an index with a key ID. In hashing, a key is assigned to specific data.
7. Divide and Conquer Algorithm: This algorithm breaks a problem into sub-problems, solves a single
sub-problem, and merges the solutions to get the final solution. It consists of the following three steps:
8. Greedy Algorithm: In this type of algorithm, the solution is built part by part. The solution for the
next part is built based on the immediate benefit of the next part. The one solution that gives the most
benefit will be chosen as the solution for the next part.
9. Dynamic Programming Algorithm: This algorithm uses the concept of using the already found
solution to avoid repetitive calculation of the same part of the problem. It divides the problem into
smaller overlapping sub problems and solves them.
10. Randomized Algorithm: In the randomized algorithm, we use a random number so it gives
immediate benefit. The random number helps in deciding the expected outcome.
Advantages of Algorithms:
It is easy to understand.
An algorithm is a step-wise representation of a solution to a given problem.
In an Algorithm the problem is broken down into smaller pieces or steps hence, it is easier
for the programmer to convert it into an actual program.
Disadvantages of Algorithms:
Writing an algorithm takes a long time so it is time-consuming.
Understanding complex logic through algorithms can be very difficult.
Branching and Looping statements are difficult to show in Algorithms
Flowchart
Flowcharts are nothing but the graphical representation of the data or the algorithm for a better
understanding of the code visually. It displays step-by-step solutions to a problem, algorithm, or
process. It is a pictorial way of representing steps that are preferred by most beginner-level programmers
to understand algorithms of computer science, A flowchart is a type of diagram that represents a
workflow or process. A flowchart can also be defined as a diagrammatic representation of an algorithm,
a step-by-step approach to solving a task.
Uses of Flowcharts in Computer Programming/Algorithms
Overall, the classification of algorithms plays a crucial role in computer science and helps to improve
the efficiency and effectiveness of solving problems.
Classification by Implementation Method: There are primarily three main categories into which an
algorithm can be named in this type of classification. They are:
1. Recursion or Iteration: A recursive algorithm is an algorithm which calls itself again and again
until a base condition is achieved whereas iterative algorithms use loops and/or data
structures like stacks, queues to solve any problem. Every recursive solution can be implemented
as an iterative solution and vice versa.
2. Exact or Approximate: Algorithms that are capable of finding an optimal solution for any
problem are known as the exact algorithm. For all those problems, where it is not possible to find
the most optimized solution, an approximation algorithm is used. Approximate algorithms are
the type of algorithms that find the result as an average outcome of sub outcomes to a problem.
3. Serial or parallel or Distributed Algorithms: In serial algorithms, one instruction is executed
at a time while parallel algorithms are those in which we divide the problem into sub problems
and execute them on different processors. If parallel algorithms are distributed on different
machines, then they are known as distributed algorithms.
Classification by Design Approaches: There are two approaches for designing an algorithm. These
approaches include
Top-Down Approach :
Bottom-up approach :
Top-Down Approach: In the top-down approach, a large problem is divided into small sub-
problem. And keep repeating the process of decomposing problems until the complex problem is
solved. Breaking down a complex problem into smaller, more manageable sub-problems and
solving each sub-problem individually. Designing a system starting from the highest level of
abstraction and moving towards the lower levels.
Bottom-up approach: The bottom-up approach is also known as the reverse of top-down
approaches. In approach different, part of a complex program is solved using a programming
language and then this is combined into a complete program. Breaking down a complex problem
into smaller, more manageable sub-problems and solving each sub-problem individually.
Designing a system starting from the highest level of abstraction and moving towards the lower
levels.
Other Classifications: Apart from classifying the algorithms into the above broad categories,
the algorithm can be classified into other broad categories like:
Randomized Algorithms: Algorithms that make random choices for faster solutions are known
as randomized algorithms. Example: Randomized Quicksort Algorithm.
Classification by complexity: Algorithms that are classified on the basis of time taken to get a
solution to any problem for input size. This analysis is known as time complexity analysis.
Example: Some algorithms take O(n), while some take exponential time.
Classification by Research Area: In CS each field has its own problems and needs efficient
algorithms. Example: Sorting Algorithm, Searching Algorithm, Machine Learning etc.
Branch and Bound Enumeration and Backtracking: These are mostly used in Artificial
Intelligence.
“Priori” means “before”. Hence Priori analysis means checking the algorithm before its
implementation. In this, the algorithm is checked when it is written in the form of theoretical
steps. This Efficiency of an algorithm is measured by assuming that all other factors, for
example, processor speed, are constant and have no effect on the implementation. This is done
usually by the algorithm designer. This analysis is independent of the type of hardware and
language of the compiler. It gives the approximate answers for the complexity of the program.
2. Posterior Analysis:
“Posterior” means “after”. Hence Posterior analysis means checking the algorithm after its
implementation. In this, the algorithm is checked by implementing it in any programming
language and executing it. This analysis helps to get the actual and real analysis report about
correctness (for every possible input/s if it shows/returns correct output or not), space required,
time consumed, etc. That is, it is dependent on the language of the compiler and the type of
hardware used.
Order of Growth
The order of growth of an algorithm is an approximation of the time required to run a computer
program as the input size increases. The order of growth ignores the constant factor needed for
fixed operations and focuses instead on the operations that increase proportional to input size.
The order of growth is often described using either Big-Theta or Big-O notation, but that
notation is out of scope for this course.
This table summarizes the most common orders of growth:
Logarithmic time
When an algorithm has a logarithmic order of growth, it increases proportionally to
the logarithm of the input size.
The binary search algorithm is an example of an algorithm that runs in logarithmic time.
Linear time
When an algorithm has a linear order of growth, its number of steps increases in direct
proportion to the input size.
Quadratic time
When an algorithm has a quadratic order of growth, its steps increase in proportion to the input
size squared.
Several list sorting algorithms run in quadratic time, like selection sort. That algorithm starts
from the front of the list, then keeps finding the next smallest value in the list and swapping it
with the current value.
Exponential time
When an algorithm has a super polynomial order of growth, its number of steps increases faster
than a polynomial function of the input size.
An algorithm often requires super polynomial time when it must look at every permutation of
values. For example, consider an algorithm that generates all possible numerical passwords for a
given password length.
All together now
Now that we've seen examples of possible run times for algorithms, let's compare them on a
graph:
Asymptotic Notations
The notations use to describe the asymptotic running time of an algorithm are defined in terms of
functions whose domains are typically the set N of natural numbers or the set R of real numbers.
Such notations are convenient for describing a running time function T(n).
Graphic examples of the O, Y, and ‚ notations. In each part, the value of n 0 shown is the
minimum possible value, but any greater value also works. (a) O-notation gives an upper bound
for a function to within a constant factor. We write f (n)= O(g(n)) if there are positive constants
n 0 and c such that at and to the right of n 0 , the value of f(n) always lies on or below c(g(n)).
(b) Ω notation gives a lower bound for a function to within a constant factor. We write
f(n)=Ω(g(n)) if there are positive constants n 0 and c such that at and to the right of n 0 , the
value of f (n) always lies on or above cg(n). (c) Θ-notation bounds a function to within constant
factors. We write f (n)= Θ (g(n)) if there exist positive constants n 0 , c 1 , and c 2 such that at
and to the right of n 0 , the value of f (n) always lies between c 1 g(n) and c 2 g(n) inclusive.
O-notation describes an asymptotic upper bound. We use O-notation to give an upper bound on a
function, to within a constant factor.
Ω -notation provides an asymptotic lower bound.
Θ -notation for asymptotically tight bounds.
Recursion
Recursion is the process of defining a problem in terms of itself.
Why Recursion Works
In a recursive algorithm, the computer "remembers" every previous state of the problem. This
information is "held" by the computer on the "activation stack" (i.e., inside of each functions
workspace).
Every function has its own workspace PER CALL of the function.
int fact(int n)
{
if (n < = 1) // base case
return 1;
else
return n*fact(n-1);
}
In the above example, the base case for n < = 1 is defined and the larger value of a number can
be solved by converting to a smaller one till the base case is reached.
Recurrences Relation
A recurrence relation is a mathematical expression that defines a sequence in terms of its
previous terms. In the context of algorithmic analysis, it is often used to model the time
complexity of recursive algorithms.
General form of a Recurrence Relation: an = f(an-1 , an-2, ……an-k)
Where f is a function that defines the relationship between the current term and the previous
terms
Recurrence Relations play a great role in developing the problem-solving skills of an individual.
Some of the common uses of Recurrence Relations are:
Time Complexity Analysis
Generalizing Divide and Conquer Algorithms
Analyzing Recursive Algorithms
Defining State and Transitions for Dynamic Programming.
Substitution Method
The substitution method is a technique used to solve recurrences, which are equations or
inequalities that describe the running time of a recursive algorithm. Recurrences are often
encountered when analyzing divide-and-conquer algorithms, such as merge sort, quicksort, or
algorithms based on recursion.
Method consists of two main steps:
1. Guess the Solution.
2. Use the mathematical induction to find the boundary condition and shows that the guess
is correct.
Iterative Method
Iteration Method for Solving Recurrences that convert the recurrence into a summation. By
iterating the recurrence until the initial condition is reached.
Let’s consider ex.
Where, c is a constant
Let's replace n with n/2 in the previous equation.
Where, c is a constant
Recursion Tree
This method, convert the recurrence into a tree and then sum the costs of all the levels of the tree.
Ex.
Here, T (n) is the running time of the entire algorithm and this running time is broken into two
terms - cn and
ere, we have represented T(n) as two sub problems and the cost cn which is the total cost except
these two sub problems.
cost of cn . This is also true for the next level i.e., its cost is also
Master Theorem
Master's method is useful for solving recurrence equations because it directly gives us the cost of
an algorithm.
Ex.
Changing Variable
The "change of variable" method is a technique used in the analysis of algorithms to solve
recurrences. It involves substituting a new variable in place of the original variable in the
recurrence relation, transforming it into a different form that may be easier to solve.
Ex.
Solving for S we see that it resolves to our familiar friend O (m logm). Now that we've solved S
we want to express this in terms of T (n). To do this, merely plug back in our original value
for m and we have T∈O(logn loglogn).
Heap Sort
Heap sort is a comparison-based sorting technique based on Binary Heap data structure. It is
similar to the selection sort where we first find the minimum element and place the minimum
element at the beginning. Repeat the same process for the remaining elements.
Heap Sort Algorithm
First convert the array into heap data structure using heapify, then one by one delete the root
node of the Max-heap and replace it with the last node in the heap and then heapify the root of
the heap. Repeat this process until size of heap is greater than 1.
Build a heap from the given input array.
Repeat the following steps until the heap contains only one element:
Swap the root element of the heap (which is the largest element) with the last element of
the heap.
Remove the last element of the heap (which is now in the correct position).
Heapify the remaining elements of the heap.
The sorted array is obtained by reversing the order of the elements in the input array.
Question Bank
1. Define an algorithm and discuss its key characteristics.
2. Explain the difference between an algorithm and a program.
3. What is the importance of analyzing algorithms in computer science?
4. Discuss the role of pseudocode in algorithm design and analysis.
5. Explain the importance of worst-case analysis in evaluating algorithms.
6. Define "big-O" notation and discuss its significance in algorithm analysis.
7. Compare and contrast the time and space complexity of two algorithms.
8. Solve the following recurrence relation using master method.
i)T(n)=4T(n/2)+n
ii)T(n)=4T(n/2)+n2
iii)T(n)=4T(n/2)+n3
iii)T(n)=4T(n/2)+n3
9. Evaluate 9T(n/3) + n
10. Define Big O notation? What is the total time complexity of following code?
int a,b,c,d,i;
{
for(i=0;i<=11;i++)
{
a=a+b;
}
d=c+a;
}
11. Define algorithm. What is the need of algorithm analysis? Which factors affect runtime
of an algorithm?
12. An algorithm requires zero or more input. Justify.
13.Solve the following recurrence using the master method. Verify solution using
substitution method. T(n)=2T(n/2)+cn
14. Explain Heapsort with example.