ALGORITHM
ALGORITHM
Hence, many solution algorithms can be derived for a given problem. The next step is
to analyse those proposed solution algorithms and implement the best suitable
solution.
1.8 ALGORITHM COMPLEXITY
Suppose X is an algorithm and n is the size of input data, the time and space used by
the algorithm X are the two main factors, which decide the efficiency of X.
Time Factor − Time is measured by counting the number of key operations
such as comparisons in the sorting algorithm.
Space Factor − Space is measured by counting the maximum memory space
required by the algorithm.
The complexity of an algorithm f(n) gives the running time and/or the storage space
required by the algorithm in terms of n as the size of input data.
1.8.1 Space Complexity
Space complexity of an algorithm represents the amount of memory space required by
the algorithm in its life cycle. The space required by an algorithm is equal to the sum
of the following two components −
A fixed part that is a space required to store certain data and variables that are
independent of the size of the problem. For example, simple variables and
constants used, program size, etc.
A variable part is a space required by variables, whose size depends on the size
of the problem. For example, dynamic memory allocation, recursion stack
space, etc.
Space complexity S(P) of any algorithm P is S(P) = C + SP(I), where C is the fixed
part and S(I) is the variable part of the algorithm, which depends on instance
characteristic I. Following is a simple example that tries to explain the concept −
Algorithm: SUM(A, B)
Step 1 - START
Step 2 - C ← A + B + 10
Step 3 - Stop
Here we have three variables A, B, and C and one constant. Hence S(P) = 1 + 3. Now,
space depends on data types of given variables and constant types and it will be
multiplied accordingly.
1.8.2 Time Complexity
Time complexity of an algorithm represents the amount of time required by the
algorithm to run to completion. Time requirements can be defined as a numerical
function T(n), where T(n) can be measured as the number of steps, provided each step
consumes constant time.
computational time is T(n) = c ∗ n, where c is the time taken for the addition of two
For example, addition of two n-bit integers takes n steps. Consequently, the total
bits. Here, we observe that T(n) grows linearly as the input size increases.
1.9 ALGORITHM ANALYSIS
Efficiency of an algorithm can be analyzed at two different stages, before
implementation and after implementation. They are the following –
A Priori Analysis or Performance or Asymptotic Analysis − This is a
theoretical analysis of an algorithm. Efficiency of an algorithm is measured by
assuming that all other factors, for example, processor speed, are constant and
have noeffect on the implementation.
A Posterior Analysis or Performance Measurement − This is an empirical
analysis of an algorithm. The selected algorithm is implemented using
programming language. This is then executed on target computer machine. In
this analysis, actual statistics like running time and space required, are
collected.
We shall learn about a priori algorithm analysis. Algorithm analysis deals with the
execution or running time of various operations involved. The running time of an
operation can be defined as the number of computer instructions executed per
operation.
Analysis of an algorithm is required to determine the amount of resources such as time
and storage necessary to execute the algorithm. Usually, the efficiency or running time
of an algorithm is stated as a function which relates the input length to the time
complexity or space complexity.
Algorithm analysis framework involves finding out the time taken and the memory
space required by a program to execute the program. It also determines how the input
size of a program influences the running time of the program.
In theoretical analysis of algorithms, it is common to estimate their complexity in the
asymptotic sense, i.e., to estimate the complexity function for arbitrarily large input.
BigO notation, Omega notation, and Theta notation are used to estimate the
complexity function for large arbitrary input.
1.9.1 Types of Analysis
The efficiency of some algorithms may vary for inputs of the same size. For such
algorithms, we need to differentiate between the worst case, average case and best
case efficiencies.
1.9.1.1 Best Case Analysis
If an algorithm takes the least amount of time to execute a specific set of input, then it
is called the best case time complexity. The best case efficiency of an algorithm is the
efficiency for the best case input of size n. Because of this input, the algorithm runs
the fastest among all the possible inputs of the same size.
1.9.1.2 Average Case Analysis
If the time complexity of an algorithm for certain sets of inputs are on an average,
then such a time complexity is called average case time complexity. Average case
analysis provides necessary information about an algorithm’s behaviour on a typical
or random input. You must make some assumption about the possible inputs of size n
to analyse the average case efficiency of algorithm.
1.9.1.3 Worst Case Analysis
If an algorithm takes maximum amount of time to execute for a specific set of input,
then it is called the worst case time complexity. The worst case efficiency of an
algorithm is the efficiency for the worst case input of size n. The algorithm runs the
longest among all the possible inputs of the similar size because of this input of size n.
1.10 MATHEMATICAL NOTATION
Algorithms are widely used in various areas of study. We can solve different problems
using the same algorithm. Therefore, all algorithms must follow a standard. The
mathematical notations use symbols or symbolic expressions, which have a precise
semantic meaning.
1.10.1 Asymptotic Notations
A problem may have various algorithmic solutions. In order to choose the best
algorithm for a particular process, you must be able to judge the time taken to run a
particular solution. More accurately, you must be able to judge the time taken to run
two solutions, and choose the better among the two.
To select the best algorithm, it is necessary to check the efficiency of each algorithm.
The efficiency of each algorithm can be checked by computing its time complexity.
The asymptotic notations help to represent the time complexity in a shorthand way. It
can generally be represented as the fastest possible, slowest possible or average
possible The notations such as O (Big-O), Ώ (Omega), and θ (Theta) are called as
asymptotic notations. These are the mathematical notations that are used in three
different cases of time complexity.
1.10.1.1 Big-O Notation
‘O’ is the representation for Big-O notation. Big -O is the method used to express the
upper bound of the running time of an algorithm. It is used to describe the
performance or time complexity of the algorithm. Big-O specifically describes the
worst-case scenario and can be used to describe the execution time required or the
space used by the algorithm.
Table 2.1 gives some names and examples of the common orders used to describe
functions. These orders are ranked from top to bottom.
Big-O notation is generally used to express an ordering property among the functions.
This notation helps in calculating the maximum amount of time taken by an algorithm
to compute a problem. Big-O is defined as:
f(n) ≤ c ∗ g(n)
where, n can be any number of inputs or outputs and f(n) as well as g(n) are two non-
negative functions. These functions are true only if there is a constant c and a non-
negative integer n0 such that, n ≥ n0.
The Big-O can also be denoted as f(n) = O(g(n)), where f(n) and g(n) are two non -
negative functions and f(n) < g(n) if g(n) is multiple of some constant c. The graphical
representation of f(n) = O(g(n)) is shown in figure 2.1, where the running time
increases considerably when n increases.
Example: Consider f(n)=15n3+40n2+2nlog n+2n. As the value of n increases, n3
becomes much larger than n2, nlog n, and n. Hence, it dominates the function f(n) and
we can consider the running time to grow by the order of n3. Therefore, it can be
written as f(n)=O(n3).
The values of n for f(n) and C* g(n) will not be less than n0. Therefore, the values less
than n0 are not considered relevant.
non-negative integer n0, such that f(n) ≥ c ∗ g(n) for all n ≥n0. Figure 2.2 shows
g(n) for all large values of n, i.e., if there exists some positive constant c and some
Omega notation.
Figure Omega Notation f(n) = Ώ (g(n))
Let us take an example to understand the Omega notation more clearly.
Example:
Consider function f(n) = 2n2+5 and g(n) = 7n.
We need to find the constant c such that f(n) ≥ c ∗ g(n).
Let n = 0, then
f(n) = 2n2+5 = 2(0)2+5 = 5
g(n) = 7(n) = 7(0) = 0
Here, f(n)>g(n)
Let n = 1, then
f(n) = 2n2+5 = 2(1)2+5 = 7
g(n) = 7(n) = 7(1) = 7
Here, f(n)=g(n)
Let n = 2, then
f(n) = 2n2+5 = 2(2)2+5 = 13
g(n) = 7(n) = 7(2) = 14
Here, f(n) )<g(n)
Thus, for n=1, we get f(n) ≥ c ∗ g(n). This concludes that Omega helps to determine
the "lower bound" of the algorithm's run-time.
1.10.1.3 Theta Notation
'θ' is the representation for Theta notation. Theta notation is used when the upper
bound and lower bound of an algorithm are in the same order of magnitude. Theta can
be defined as:
c1 ∗ g(n) ≤ f(n) ≤ c2 ∗ g(n) for all n>n0
Where, n is any number of inputs or outputs and f(n) and g(n) are two nonnegative
functions. These functions are true only if there are two constants namely, c1, c2, and
a non-negative integer n0.
Theta can also be denoted as f(n) = θ(g(n)) where, f of n is equal to Theta of g of n.
The graphical representation of f(n) = θ(g(n)) is shown in figure 2.3. The function f(n)
is said to be in θ (g(n)) if f(n) is bounded both above and below by some positive
constant multiples of g(n) for all large values of n, i.e., if there exists some positive
constant c1 and c2 and some non-negative integer n0, such that C2g(n)≤f(n)≤ C1g(n)
for all n≥n0.
SUBSTITUTION METHOD
The substitution method for solving recurrence relations consists of three
steps:
1. Guess the form of the solution.
2. Verify by induction.
3. Solve for constants.
Our desired form was , so we tried to put the equation in the form desired –
residual: . This is certainly less then our desired form
whenever ≥ (residual), for example, if c≥2 and n≥1.
3.For 1≤n<n0, we have if we pick c large enough. And so we’ve
But there is no c>0 such that . It seems like we’ve hit a dead
end! But we can actually strengthen the inductive hypothesis by subtracting a low-
order term.
As you can see, an important part of the substitution method involves making
a good guess. But unfortunately, there is no general way to guess the correct
solutions to recurrences. If a recurrence is similar to one you’ve seen before,
then guessing a similar solution is reasonable.
RECURSION TREE METHOD
Since the substitution method involves having to guess the solution, it might
often be difficult to effectively use it. We can use recursion trees to devise a
good guess. In a recursion tree, each node represents the cost of a single
subproblem somewhere in the set of recursive function invocations. We sub
the costs within each level of the tree to obtain a set of per-level costs, and
then we sum all the per-level costs to determine the total cost of all levels of
recursion.
So let’s use an example to show the recursion tree method! Suppose we
wanted to solve
Guessing a correct solution might be difficult and expanding the recurrence
might be difficult, so let’s draw a recursion tree.
The recursion tree will represent the sum of the number of steps the
recurrence has to do on each level. From the original recurrence equation
We can keep expanding the tree, and eventually we’ll reach a base case. (We’ll
assume T(1)=Θ(1).)
Now let’s count the cost on each level. We’ll just sum the number of nodes.
The total cost will be We’ll assume that the
ALGORITHMS AS A TECHNOLOGY
Suppose computers were infinitely fast and computer memory was free. Would you
have any reason to study algorithms? The answer is yes, if for no other reason than
that you would still like to demonstrate that your solution method terminates and
does so with the correct answer.
If computers were infinitely fast, any correct method for solving a problem would
do. You would probably want your implementation to be within the bounds of good
software engineering practice (for example, your implementation should be well
designed and documented), but you would most often use whichever method was
the easiest to implement.
Of course, computers may be fast, but they are not infinitely fast. And memory may
be inexpensive, but it is not free. Computing time is therefore a bounded resource,
and so is space in memory. You should use these resources wisely, and algorithms
that are efficient in terms of time or space will help you do so.
Efficiency
Different algorithms devised to solve the same problem often differ dramatically in
their efficiency. These differences can be much more significant than differences
due to hardware and software.
As an example, we will see two algorithms for sorting. The first, known as
insertion sort, takes time roughly equal to to sort n items, where is a
constant that does not depend on n. That is, it takes time roughly proportional to .
The second, merge sort, takes time roughly equal to , where stands for
is another constant that also does not depend on n. Insertion sort
typically has a smaller constant factor than merge sort, so that .We shall see
that the constant factors can have far less of an impact on the running time than the
dependence on the input size n. Let’s write insertion sort’s running time as
and merge sort’s running time as Then we see that where insertion sort has
a factor of n in its running time, merge sort has a factor of lg n, which is much
smaller. (For example, when n D 1000, lg n is approximately 10, and when n equals
one million, lg n is approximately only 20.) Although insertion sort usually runs
faster than merge sort for small input sizes, once the input size n becomes large
enough, merge sort’s advantage of lg n vs. n will more than compensate for the
difference in constant factors. No matter how much smaller is than , there will
always be a crossover point beyond which merge sort is faster.