Asymptotic Notations
Asymptotic Notations
Table of Contents
  1. An Introduction to Asymptotic
     1.1: Introduction
     1.2: What is an Algorithm?
     1.3: Asymptotic Analysis: Why we need it?
                   1.3.1 Asymptotic Analysis: Role in Algorithm Efficiency Analysis
                   1.3.2 Asymptotic Analysis: Role in Algorithm Comparison
     1.4:Asymtotic Growth Rates
     1.5:A Simple Example : Analysis of Algorithms
1.1 Introduction
Asymptotic notations are mathematical way of describing the order of growth and limiting
behavior of a function when the argument to the function tends towards a particular value
or infinity. Asymptotic notation simplifies functions in order to concentrate on their growth
rates. We can also map, the time or space taken by an algorithm in terms of a
mathematical function of input size n for large n. Asymptotic notations can be used to
measure running time of an algorithm as well as storage memory requirements by the
algorithm, i.e. time complexity and space complexity measures of an algorithm. The
notation works well to compare algorithm efficiencies because the growth of rate of a given
algorithm approximates the value of a standard function.
We just used a term called algorithm. Let’s first see and understand what an algorithm is, in
the next section. Then we go in to the details of how asymptotic notations relate to the
notion of algorithm.
                         ALGORITHM
    INPUT                                                                    OUTPUT
                           (Method)
Value addition: Historical/Intellectual Context
Body text:
The word algorithm comes from the name of the 9th century Persian Muslim
mathematician Abu Abdullah Muhammad ibn Musa Al-Khwarizmi. The word
algorism originally referred only to the rules of performing arithmetic using Hindu-Arabic
numerals but evolved via European Latin translation of Al-Khwarizmi's name
into algorithm by the 18th century. The use of the word evolved to include all definite
procedures for solving problems or performing tasks.
Source: http://www.scriptol.com/programming/algorithm-history.php
Body text:
1.Euclid   has   created    an    algorithm    that has    been     given             its   name.
The algorithm serves to calculate the greatest common divisor, here it is:
Source: http://www.scriptol.com/programming/algorithm-history.php
An algorithm must:
          •   Be composed of a series of finite and concrete steps that solves the problem.
          •   Have no ambiguity as to which step will be performed next.
          •   Terminate.
A given problem can have many algorithms (methods) to solve it. But it’s our responsibility
to choose the best method. Only writing a working program is not good enough. The
program may be inefficient. If the program is run on a large data set, then the running time
becomes an issue. For this we need to do analysis of an algorithm.
Source: Kenneth Rosen, Discrete Mathematics and Its Applications, Sixth Edition.
Input: Number N
Steps:
If the modulo of the input number N with two (i.e. N % 2) is 1,
   Then the input number is odd.
Otherwise it is even.
Output: No is even or odd.
We have an array with a list of numbers, and our goal is to find the maximum/minimum value from
that list of numbers. (Let’s focus only on the maximum case. The minimum case is simply the
opposite.)
Following is a more detailed form of our array with index positions added:
0 1 2 3 4 5 6  indexes
So now, our job is to start off at the beginning and find out what the maximum value is.
We set the number in the zero-th position as a temporary maximum.
In other words, the 11 at position 0 is the current maximum number. We then move to
the next number position 1, and ask, "Is 5 greater than the current maximum, 11?" The
answer is no. 5 is not greater than 11, so 11 remains our current, local maximum.
Then we move to index position 2. At position 2, the value in our array is a 73. Again we
ask the similar question, "Is 73 greater than the current maximum, 11?” The answer is
yes. 73, is greater than 11, so now our new maximum is 73.
We will keep progressing through each index position and asking whether the number at
this position is greater than the current maximum. If the number is greater, then we
replace our old maximum value with the new number we are at. If the number is not
greater, we proceed to the next number and ask if that number is greater. We keep
going until we reach the end of array.
When we reach the final value in array, we declare that whatever maximum value we
have after last value, is the maximum value of our entire array.
For finding the minimum, instead of asking, "Is this number greater than the current
maximum?” we would ask, "Is this number less than the current minimum?"
The algorithm described above are in a form, called High-level description of an algorithm
i.e. Pseudo code. It is more structured than English prose but less detailed than a program
written in a programming language. It is preferred notation for describing algorithms as it
hides program design issues
The simplest example is when we have a function f (n) and wish to describe its properties
when n becomes very large. Thus, if f (n) = n2+7n, the term 7n becomes insignificant
compared to n2 when n is very large. We say that "f (n) is asymptotically equivalent to n2 as
n → ∞" and write f (n) ~ n2.
Space complexity: represents the amount of storage space needed to solve the given
problem.
Time complexity: represents the amount of time needed to solve the given problem i.e.
the number of steps/basic operations needed as a function of the problem size.
The input size depends on the problem being studied, but in most cases it is the number of
items in the input.
y = m*x + c
Let cost (time taken by primitive operation) of each operation is c1, and then total cost is
3.c1.
Each simple operation takes constant time. Example: arithmetic (add, subtract, multiply,
divide, assignment etc) data movement (load, store) and control (conditional and
unconditional branch).Loops and subroutine calls do not take constant time.
 Many times we do not aim at obtaining an exact step count. Instead, we try only to get
asymptotic bounds on the step count. Asymptotic analysis makes use of the O (Big Oh)
notation. Other notational constructs used in analysis of algorithms are Θ (Big Theta)
notation, Ω (Big Omega) notation etc. We will soon see these notations in upcoming
lectures.
The complexity of an algorithm is expressed in terms of a function g(n) that gives the
upper/lower bound on the number of operation (or running time) performed by an algorithm
when the input size is n.
Best-case Complexity: The best case for an algorithm is the arrangement of input data for
which this algorithm performs best .It is the case where least numbers of resources (e.g.
running time, memory) are required by an algorithm.
To compare the efficiency of algorithms, the most common way of qualifying an algorithm is
the asymptotic notations.
Body text: Let’s consider another problem of finding kth largest element in list of
given N numbers. We try to generate sequence of steps to solve the given problem.
Algorithm I
Steps:
Is there any other way by which the given problem can be solved? We can definitely
explore more ways. Let’s see another way of solving the same problem.
Algorithm II
Steps:
(1) Read the first k elements into an array and we sort them in decreasing order.
(2) Then each remaining element is considered and read one by one.
Else, it is placed in its correct place in the array, moving one element out of the array.
Thus we observe that we can have different algorithms for the same problem set. But
which one will be efficient will depend on values of N and k.
 Consider two algorithms, A1 and A2, for solving a given problem. Let running times of
each of the algorithm be TA1 (n) and TA2 (n) respectively where n is input size. It would be
simple to compare two functions TA1 (n) and TA2 (n) to determine which algorithm is more
efficient if we use asymptotic notations.
If input size is n, then an algorithm A1 can take 100n steps, or A2 takes n2 steps, which one
is better? We will see that for smaller range of numbers [1,100], n2 is better running time, but for
larger range [101, ∞], the first 100 n is a better option.
Thus in order to compare the efficiency of the two algorithms, we need to compare their time
complexity for asymptotically large value of n. Hence, an algorithm with running time
TA1 (n) is more efficient than another algorithm with running time TA2 (n) if and only if, beyond
some value of n, TA2 (n) is more than TA1 (n) and the ratio TA2 (n) /TA1 (n) increases with
increase in the value of n.
Function Name
C Constant
Log N Logarithmic
N Linear
N2 Quadratic
N3 Cubic
2n Exponential
N          1             Log N          N              N2               N3           2n
1          1             0.00           1              1                1            2
10         1             3.32           10             10               1000         1024
                                                                                                30
100        1             6.64           100            10000            1000000      1.2 X 10
                                                                                                301
1000       1             9.97           1000           1000000          1000000000   1.1 X10
Input: a, b
Output: c, i.e. sum of input variables
Start
a = 100                      I
b = 200                      II
c = a+b                      III
Print c                      IV
End
Approximate, how long does this algorithm take to execute? Considering a typical line of
code, let’s say an assignment statement such as a = 1, addition operation and printing a
value, the time line of code takes to execute is some constant, C. Then the above algorithm
will take approximately C+C+2C+C = 5 C.
How did we come up with this number? It’s this simple – there are 4 lines of code. Every
line of code in the above algorithm takes some constant, C except III line which takes 2C as
two primitive operations (addition and assignment), therefore time to execute –add up to
5C’s.
But there may be case where we don’t know exactly how many lines of code get executed.
For example, algorithms having looping constructs like for loop, while loops etc.
Input: a, b
Output: c, i.e. sum of input variables displayed n number of times
Start
x=0                                         I
While x < n, do the following steps:        II
       a = 100                              III
       b = 200                              IV
       c=a+b                                V
       Print c                              VI
       x = x+1                              V II
End the while                               VIII
End the program
In this case, we don’t know how many times the while loop will execute, because we don’t
know the value of n. We can estimate that the loop execution time is: 7C * n (i.e. Nested
lines of code: III and IV and VI execute C times each and V and VII both 2C times each).I
and VIII takes C time each. Overall algorithm execution time: 2C + 7Cn.
Asymptotic notations can conveniently be used for describing running time and analysis of
algorithms. We will introduce notations in upcoming lectures.
Heading text: Mind Boggling: Input size and Time factors for an
algorithm
Body text:
Suppose, an algorithm performs like, f(n) = 2n. In T seconds, you can process k inputs.
If you get a computer 26 times faster, how many inputs can you process in T seconds?
Let’s see a mathematical solution.
We are given 2k/T operations per second.
Now (2k * 26) /T operations per second are to be performed. Let’s say Input size (No:of
inputs) be, S that can be processed now in T seconds is as follows:
2S / (27 k/T) operations per second = T seconds
Therefore, 2S = 27 k => S = 26 k => S = 64 k
Thus input’s S that can be processed in T seconds is 64 k as expressed in terms of k.
2.1 Introduction
 Asymptotic analysis of algorithms makes use of different asymptotic notations which
represent asymptotic upper and lower bounds that may or may not be asymptotically tight.
The growth of functions is often described using these special notations. Now we will
introduce several types of asymptotic notations which are used to compare the performance
and efficiency of algorithms. These notations are as follows:
In this chapter we will learn about asymptotic tight bound notations and their usage.
2.2.1 O-notation
Let us consider two functions g (n) and f (n) mapped from the set of real numbers to real
numbers, we define O (g (n)), big-Oh notation, as the set:
O (g (n)) = {f (n): there exists positive constants c and n0, such that n  n0,            we
have 0  f (n)  c g (n)  n > n0.}
It is set of all functions whose rate of growth is the same as or lower than that of g (n).It is
also called asymptotic upper bound.
Choose some constant c and some value n0 such that for every value of n larger than n0,f
(n)  cn2 i.e. for values larger than n0, f(n) is never more than a constant multiplier greater
than n2 that implies f(n) does not grow more than a constant factor faster than n2.
Answer is “Yes”. n3 grows faster than n2, so n3 grows also faster than f(n).
Therefore, we always try to find the smallest simple function g(x) for which f(x) is          O
(g(x)).
Body Text: Big-O notation was introduced in P. Bachmann’s 1892 book Analytische
Zahlentheorie. He used it to say things like “x is O (n/2)” instead of “x ~ n/2.” The
notation works well to compare algorithm efficiencies because we want to say that the
growth of effort of a given algorithm approximates the shape of a standard function.
The big-O symbol is sometimes called a Landau symbol after the German mathematician
Edmund Landau, who used this notation throughout his work. The use of big-O notation
in computer science was popularized by Donald Knuth, who later on also introduced the
big-  and big- notations.
Source: http://en.wikipedia.org/wiki/Big_O_notation
 f (n) is O (g (n)) is sometimes written as f (n) = O (g (n)). This use of the equals sign is
an abuse of notation, as the above statement is not an equation. However, it is
acceptable to write f (n) E O (g (n)) i.e. f (n) belongs to O (g(n)).
It is improper to conclude from g (n) = O (f(n)) and h(n) = O(f(n)) that g(n) and h(n)
are equal. One way to think of this is to consider "= O" one symbol here. This notation
tells us that an inequality holds relating the values of the functions f and g for sufficiently
large numbers.
Source:http://www.statemaster.com/encyclopedia/Big-O-
notation#Equals_or_member-of_and_other_notational_anomalies
Examples:
3n + 7  c n
 (c 3) n  7
 n  7/(c  3)
Therefore c = 4 and n0 = 7
7n2  c n
 n  c/7
     The above inequality cannot be satisfied since c/7 must be a constant but n is not
     fixed.
     Thus we verified function 7n2 is not O (n).Also, we talk of larger values for
     asymptotic notation where n is being limited on upper side.
n2 + 2n + 1  n2 + 2n2 + n2
 n2 + 2n + 1  4n2
Therefore, c = 4 and n0 = 1.
n! = n(n-1)(n-2)……………………321
>= (n/2)n/2
=n/2(lgn – 1)
<=nlgn
=O(nlgn)
Proof: Each of the terms in the summation is of the form ai ni. Since n is non-negative,
a particular term will be negative only if ai < 0. Hence, for each term in the summation,
Recall too that we have stipulated that the coefficient of the largest power of n is
positive, i.e. ai > 0
So for n>= 1,
Body Text: Big-O notation is used to estimate the number of operations needed to
solve a problem using a specified procedure or algorithm. Following holds for some of
the most common functions used for analysis of algorithms:
O (1) < O (log n) < O (n) < O (n log n) < O (n 2) < O (2n)
Source :
http://media.photobucket.com/image/big%20O%20graph/amit_9b/BIGO_GRAPH.jpg
2.2.2 Ω notation
Let us consider two functions g (n) and f (n), we define  (g (n)), big-omega notation, as
the set:
 (g (n)) = {f (n): there exists a positive constants c and n0, such that n  n0,
we have 0  c g (n)  f (n)  n > n0         }
The definition of omega is almost identical to that of big oh. The only difference is in the
comparison, for big oh it is f (n)  c g (n) but for omega, it is c g (n)  f (n).
Examples:
 n2  n2 + 2n + 1
 0 2n + 1
i.e. c n  n2
                0  n2 - n
                0  n (n-1)
                1 n
Hence the constants n0 and c’ such that c’f (n)  g (n) where c’ = 1/c exist and therefore
g(n) is  (f(n)).
The answer is yes again, If f (n) ∈  (g(n)),then there exists a constant c and n0such that,
for all n ≥n0,
Hence the constants n0and c’ such that c’f (n) ≥ g (n) where c’ = 1/c exist and therefore g
(n) is O (f(n)).
2.2.3 -notation
Again we consider two functions g (n) and f (n), we define (g (n)), big-Theta, as the set:
(g (n)) = { f(n) : such that there exists positive constants c1,c2 and n0, such that
n  n0, we have 0  c1g(n)  f(n)  c2g(n) }
Function f (n) is sandwiched between c1 g(n) and c2g(n) for sufficiently large n. Big-Theta
means the bound is the tightest bound possible. We write
f (n) = (g(n))  n > n0 where f(n) and (g(n)) are positively asymptotic.
Examples:
              c1 n  4n + 3  c2 n      n > n0
               Let’s say we choose c1 = 1, c2= 5, positive constants
              n 4n + 3 5n
               Considering above, two inequalities we get following equations:
               3n+3 > 0 and 3  n
              n = -1 and n >= 3 Therefore n0 = 3.
              c1 n  4n2  c2 n     n > n0
              c1  4n  c2           n > n0
               Considering above, two inequalities we get following equation:
               c1 /4  n  c2 /4,
               This cannot possibly hold for arbitrarily large n.
Body Text:
f n   Og n        wt
f n   g n        wt
f n   g n        wt
Big-O bound is only an upper bound. So an algorithm that is O (n3) might not ever take that
much time. It may run in O (n) time.
Big-omega bound is only a lower bound. Big  bound is precise. So, if an algorithm is
 (n2), it runs in quadratic time.
Body Text: We will prove for any two functions g(n) and f(n),
                       f(n) = (g(n)) iff f(n) = O(g(n)) and f(n) = (g(n)).
In practice, asymptotically tight bounds are obtained from asymptotic upper and lower
bounds, i.e. (g (n)) = O (g (n)) intersection  (g(n)).
O (g (n)) = {f (n): there exists a positive constants c1 and n0, such that n  n0,    we
have 0  f (n)  c1 g (n)  n > n0.}
And
 (g (n)) = {f (n): there exists a positive constants c2 and n0, such that n  n0,    we
have 0  c2 g (n)  f (n)  n > n0 }
O (g(n)) intersection  (g(n)) = { f(n) : such that there exists positive constants c1,c2
and n0, such that n  n0, we have 0  c1g(n)  f(n)  c2g(n) } which is actually (g(n))
by definition.
(g (n)) = { f(n) : such that there exists positive constants c1,c2 and n0, such that n 
n0, we have 0  c1g(n)  f(n)  c2g(n) } [from definition]
ii) c1 g(n)  f(n)  n > n0 there exists positive constants c1 and n0.
From above we can see that L.H.S = R.H.S. Hence we proved that f (n) = (g (n)) iff
f (n) = O (g (n)) and f (n) = (g (n)).
Also now,
= n2 /4.
This shows that f (n) is  (n 2). We conclude that f (n) is of order n 2, or in symbols,
f (n) is  (n 2).
Source: Kenneth Rosen, Discrete Mathematics and Its Applications, Sixth Edition
3.1 Introduction
In this chapter we will introduce loose asymptotic bounds. Also we will see some properties
of asymptotic bounds. In the later half we will see different programming constructs and
how to associate them with asymptotic notations for calculating their complexities. Thus this
chapter explores more relatives of Big Oh notation, interesting properties and application of
asymptotic notation in programming world.
Let’s consider two functions f(n) and g(n), the set little-o can be defined as:
o (g(n)) = {f(n):  c > 0,  n0 > 0 such that  n  n0, we have 0  f(n) < cg(n)}.
Function f (n) becomes insignificant relative to g (n) as n approaches infinity. The function g
(n) is an upper bound for f (n) that is not asymptotically tight.
However, for little-oh notation, we need to show that for all c, there exists an n0 such that 0
<= f (n) < c*g (n). So finding a specific c and n0 isn't enough to prove this as was in Big Oh
notation.
Let’s consider two functions f(n) and g(n), the set little-o can be defined as:
 (g(n)) = {f (n):  c > 0,  n 0 > 0 such that  n  n0, we have 0  cg(n) < f(n)}.
The function g (n) is a lower bound for f (n) that is not asymptotically tight.
Here also according to definition we need to show that for all c, there exists an n0 such that
0 <= f (n) < c*g (n).Therefore we again introduce the following definition:
                           3
Example: To Prove 4x           + 5x 2 – 6 =  (x 2).We verify as follows:
         4 x 3  5x 2  6        4x  5  6 / x 2
    lim                    lim                   
    x         x2          x        1
There exists a limit value =∞. Hence small  relationship is proved.
You must be thinking could we have applied similar limits formulas to asymptotic tight
bounds. What you think? Can we express them as well in a mathematical formula with
application of limits?
Answer is definitely yes, as they relate to growth of functions. Look at the following:
n! = n (n-1)………321
           n
      =n       (1-1/n)……..3/n.2/n.1/n
 f n   og n       wt
f n    g n       wt
Body text:
Body text:
Transitivity
Reflexivity
f(n) = (f(n))
f(n) = O(f(n))
f(n) = (f(n))
Symmetry
Complementarities
Monotonicity of Functions
A function f (n) is
 If f1(x) is O(g1(x)) and f2(x) is O(g2(x)), then (f1 + f2)(x) is O(max(g1(x), g2(x)))
Body text:
Let f(x) and g(x) be functions from a X to Y, both set of real number. Then
max (f(x), g(x)) is the function from X to Y that takes as its value at each point x the
larger of f(x) and g(x).
MAX
g (x)
f (x)
Body Text:
                                       a  b logb a
        1
a 1                                  log c ( ab)  log c a  log c b
        a
                                       log b a n  n log b a
(a m ) n  a mn
                                                       log c a
a m a n  a m n                       log b a 
                                                       log c b
lim
     nb
         0                            log b (1 / a )   log b a
n  a n
                                                         1
 n b  o( a n )                       log b a 
                                                       log a b
                                       a logb c  c logb a
             Code Statement 1;
             Code Statement 2;
              ...
             Code Statement n;
Total time Taken = time (statement 1) + time (statement 2) + ... + time (statement n)
If each statement only involves basic operations as discussed above, then the time for each
statement is constant and the total time is also constant: O (1).
       if (condition)
       {
                    Sequence of coding statements
       }
       else
       {
                   Sequence of coding statements
       }
Either sequence in if condition will execute, or sequence in else condition will execute.
Therefore,  the   worst-case    time    is   the   slower  of   the    two  possibilities:
max (time (sequence 1), time (sequence 2)). For example, if sequence 1 is O (1) and
sequence 2 is O (n) the worst-case time for the whole if-then-else statement would be O
(n).
c) For loops
The loop executes n times, so the sequence of statements also executes n times. We
assume the statements are O (1), the total time for the ‘for’ loop is n * O (1), which is
O (n).
d) Nested loops
Case 1: Number of iterations of the inner loop is independent of the value of the outer
loop's control variable.
The outer for loop executes N times. For each execution of the outer loop, the inner loop
executes M times. Thus, the statements in the inner loop execute a total of N * M times.
Thus, the complexity is O (N * M).
Case 2: Number of iterations of the inner loop depends on the value of the outer loop's
control variable. For example:
So we can see that the total number of times the sequence of statements executes is:
N + N-1 + N-2 + ... + 3 + 2 + 1
= N (N+1)/2
= O (N2).
e) While loops
            i=0
         while (i < n)
         {
               Sequence of coding statements
                               i++; }
The loop executes n times, so the sequence of statements also executes n times. Since we
assume the statements are O (1), the total time for the while loop is n *(O (1)), which is
O (n) on a whole.
In above example, control variable of for loop increases by multiplicative factor of 2 each
time.
       Here, i = 2, 22, 23… 2k-1
       So 2k-1  n
        log(2k-1)  log(n)
        k-1  log(n)/log(2)
        k = log(n)/log(2))+1
Therefore the run time is O (log (n)).
2. for(int i = 0; i<n; i += 2)
{
                 Basic operation statement;        // takes O (1)
}
In above example, control variable of for loop increases by 2 each time, but not by a
multiplicative factor of 2. So it’s not log (n).
       Here, i = 0, 2, 4, 6, 8 … So the given loop will run for n/2 iterations. Therefore
the run time is O (n).
The number of iterations of the inner loop depends on the value of the index of the outer
loop. In this example the inner-loop index is counting down from N to i+1. It is the case
that the inner loop executes N times, then N-1, then N-2, etc, so the total number of
times the innermost "statement" executes is O (N2).
After so much exposure to asymptotic notations, we can now use them for calculating
complexity of an algorithm for a given problem.
Body text:
Problem definition: To compute maximum difference between any two numbers in the
input list of items.
Algorithm 1
diff := 0
for y := x + 1 to n
Algorithm 2
min := a1
max := a1
for x := 2 to n
4.1 Introduction
In this chapter we explore applying asymptotic notations in measuring the efficiency of
some famous algorithms. It’s an important question that “How efficient an algorithm is?”
Efficiency focuses on resources like CPU (time), memory, network and disk usage. Our main
concern is measuring the resource requirements. Depending upon the machine/compiler as
well as coding structure of an algorithm, we can say performance of an algorithm is how
many actual resources being used. But how do these resource requirements scales with
increasing problem/input size is basically, Complexity of an algorithm i.e. how the number
of operations relates to the problem size.
Problem: Given a list of items a0, a1... an-1 and an item K that is to be searched in the list,
return an index value i such that ai == K; if K is not in the list, return “not found”.
Pseudo-Code:
Linear Search (List, ItemValue)
Begin
Loc = 0; //initialize Loc
While ((List [Loc]! = ItemValue) AND (Loc < List. Length))
         Loc = Loc + 1;
If Loc<=List Length, return Loc;
Else return -1;
End
5 3 11 10 9 15
5 3 11 10 9 15
11
5 3 11 10 9 15
11
5 3 11 9 15
11
    Asymptotically, therefore, the worst-case cost and the expected cost of linear search are
    both O (n).
This algorithm compares the ItemValue to be searched with the middle item of the input list
and if they are equal then we say the search is over. If it is greater than the middle item,
then we search in the right half recursively, otherwise we search in the left half recursively
until the middle item is equal to the input item.
Pseudo-Code:
Binary Search (List [], int ItemValue) {
       int low = 0;
       int mid;
       int high = List.length - 1
       while (low <= high) {
               mid = (low + high)/2;
               if (ItemValue < List [mid])
                        high = mid -1; // search in lower half
               else if (List [mid] < ItemValue)
                        low = mid + 1; // search in upper half
               else return mid; // success
       }
       return -1; // ItemValue is not in the array
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Mid Element
•          Best case: Occurs when ItemValue to be searched is found at the middle position
    examined in the given list. Therefore number of comparisons is 1.Thus O (1).
•          Worst case: Occurs when ItemValue to be searched is not in the list. While Loop
    executes until low <= high and size is halved in each iteration i.e.
           N, N/2, N/4…………… N/2K…. 1 which gives an estimate like follows:
           => N/2K = 1 i.e. 2K = N
           => K=log2N steps;at each step no: of comparisions = 1. Thus gives total of O (log2
    (N)) comparisions.
•          Average case :We analyze iteration by iteration and find the number of
    comparisons as follows:
Asymptotically, therefore, the worst-case cost and the expected cost of binary search are
both O (log2 (N)).
Bubble sort is a simple sorting algorithm. It works by repeatedly scanning through the list to
be sorted, comparing each pair of adjacent items and swapping them if they are not in the
right order. The pass through the list is repeated until no swaps are needed, which indicates
that the list is sorted. The algorithm is named after the behavior, smaller items "bubble" to
the top of the list.
Pseudo-Code:
Bubble Sort (List []) {
      for i = 1 to List.length {
              for j = 1 to to List.length – i {
                      if List[j] >List[j+1]
                             swap(A[j] and List[j+1])} } }
PASS 1
                                               NO       SWAP required
  3        5     11 10 2 1
                                               NO       SWAP REQUIRED
  3        5     11 10 2 1
                                                  SWAP REQUIRED
 3       5   10 11 2 1
                                                        SWAP REQUIRED
 3       5   10 2         11 1
                                                         SWAP REQUIRED
 3       5   10 2           1 11
PASS 2
     NO SWAP REQUIRED
 3       5   10 2           1 11 NO                     SWAP REQUIRED
                                                       NO SWAP REQUIRED
 3       5   10 2 1               11
                                                      SWAP REQUIRED
 3       5   2   10 1             11
                                                      SWAP REQUIRED
 3       5   2   1         10 11
PASS 3
3 2 5 1 10 SWAP REQUIRED
   1111
                                                          SWAP REQUIRED
  3       2      1       5         10 11
PASS 4
2 3 1 5 10 11 SWAP REQUIRED
2 1 3 5 10 11 SWAP REQUIRED
PASS 5
1 2 3 5 10 11 SWAP REQUIRED
Considering the above pseudo code, the analysis for worst-case, best-case and average
case is all the same.
We can see that the outer loop runs n times. The only complexity in this analysis is in the
inner loop. Inner loop can never loop more than n times. Since the outer loop will make the
inner loop complete n times, the maximum number of comparisons are not more than O
(n2) times.
The first time the inner loop runs, it will make n-1 comparisons, the next time it will make
n-2 comparisons; and so on. Therefore, the actual number of comparisons is
    Asymptotically, therefore, the worst-case cost and the expected cost of bubble sort are both
    O ((N2)).
    It works by taking elements from the list one by one and inserting them in their correct
    position into a new sorted list.
Pseudo-Code:
     7
             10
                    14
5                         20
    C                                    Correct Place for 7 is in between 5 and 10.
    5          3         11         10         9           15
                               Institute of Lifelong learning, University of Delhi
                                       Asymptotic Notations
3 5 11 10 9 15
3 5 11 10 9 15
3 5 10 11 9 15
3 5 9 10 11 15
3 5 9 10 11 15
for j = 2 to n                                                          c1               N
  {
         do key ← List [ j ]                                             c2              N-1
                Insert List [ j ] into the sorted sequence List [1 . . j -1]              0         N-1
i←j-1 c3 N-1
                                                                                 
                                                                                      N
                                                                                             tj
                  while i > 0 and List [i] > key                          c4          j 2
                                                                                  t             1
                                                                                  N
                     do List [i + 1] ← List [i]                           c5      j 2       j
                                                                                  t             1
                                                                                  N
                      i←i–1                                                c6     j 2       j
T (n)  c1n  c2 (n  1)  c4 (n  1)  c5  t j  c6  t j  1  c7  t j  1  c8 (n  1)
                                            n           n               n
j 2 j 2 j 2
Best case: Occurs when input list is already sorted. Insertion sort will iterate once through
the list, and, finding no items found out of order, so it will not shift any of the data. Thus it
is O (N).
As already sorted array, t j  1 T(n) = c1n + c2(n -1) + c4(n -1) + c5(n -1) + c8(n-1) = (c1 + c2 + c4 +
c5 + c8)n + (c2 + c4 + c5 + c8)
= an + b = (n)
Worst case: Occurs when the smallest item of the list is at the large end that is listed is
sorted in reverse order.
                                                  n(n  1)         n(n  1)      n(n  1)
      T (n)  c1n  c2 (n  1)  c4 (n  1)  c5           1  c6           c7           c8 (n  1)
                                                  2                   2             2
       an2  bn  c
      = (n2)
Average case: The average case is also quadratic O (N2), which makes insertion sort
impractical for sorting large arrays.
Asymptotically, therefore, the worst-case cost and the expected cost of insertion sort are
both O ((N2)).
We just discussed famous sorting algorithms. There are more sorting algorithms like quick
sort, merge sort etc. Reader is being suggested to read and analyze them as well.
Summary
 •   Asymptotic Notation mathematically decides limiting behavior of a function.
 •   An algorithm is step wise procedure for solving a given problem.
 •   Asymptotic notations are important in analysis of algorithms i.e. measuring efficiency
     and comparing the efficiencies of different algorithms.
 •   Categorization of algorithms is based on their asymptotic growth rate.
 •    Asymptotically analysis is measure of the execution of an algorithm, usually the time
     or memory needed, given the problem size n, which is usually the number of items.
 •   Asymptotic upper and lower bounds can be asymptotically tight or non-tight.
 •   Tight bounds are Big oh, Big omega, Big Theta.
 •   Big oh: O (g (n)) = {f (n) n  n0, 0  f (n)  c g (n)  n > n0 and n, c>0}.
 •   Big Omega (g(n)) = {f (n) n  n0, 0  c g (n)  f (n)  n > n0 and n, c>0}.
 •   Big Theta ( g(n)) = {f (n) n  n0, 0  c1g(n)  f(n)  c2g(n)  n > n0 and n, c
     c1>0, c2 >0}.
 •   We have to find constants c, c1>0, c2 >0 and value of n  n0 which satisfy the above
     equalities to satisfy the notations.
 •   f (n) = (g (n)) if and only if f (n) = O (g (n)) and f (n) = (g (n)).
 •   Little o: o (g(n)) = {f(n):  c > 0,  n0 > 0 such that  n  n0, we have 0  f(n) <
     cg(n)}.
 •   Little omega:    (g(n)) = {f (n):  c > 0,  n       0   > 0 such that  n  n0, we have 0  c
     g(n) < f(n)}.
• Searching and Sorting are two famous problems from algorithm’s perspective.
• Comparisons of Complexities:
Exercises
1.    Define asymptotic notation.
2.    Describe an algorithm that interchanges the values of the variables x and y, using
        only assignments. What is the minimum number of assignment statements needed
        to do this? Give analysis in terms of primitive operations performed.
3.    A computer performs 109 operations per second, what is the largest problem you can
        solve in an hour if your algorithm takes 3n operations on problem of size n?
4.    What is importance of asymptotic analysis?
5.    Describe an algorithm that takes as input a list of n integers in non decreasing order
        and produces the list of all values that occur more than once.
6.    If input size is n, then an algorithm A can take 1000n steps, and other B takes n3
        steps, which one is better? Justify for different values of n.
7.    List the properties of an algorithm. Take an example of an algorithm and justify
        whether these properties are satisfied.
8.    What are the factors which affect the running time of an algorithm?
9.    What is smallest value of n such that an algorithm whose running time is 100 n2 runs
        faster than an algorithm whose running time is 2n on the same machine?
10.   Describe an algorithm for finding both the largest and the smallest integers in a finite
        sequence of integers.
11.   Devise an algorithm that finds the sum of all the integers in a list.
12.   Arrange the following in order of their growth rates values of n:
      a) n3
      b) log n
      c) 2n
      d) n log n
      e) 22n
      f) 100 n
      g) N
13.   Using definitions of asymptotic notations, prove or disprove the following:
             5n 2  7n  20  O n 2      
             n   2.1
                          
                       O n   2
             x /1000 + 3x 3 + 7x 2 – 9 = (x 4)
                 4
       a. for i = 1 to n
       {sum = sum + i;}
for i = 1 to n2
{for j =1 to i
       b. for i = 1 to n
       { for j =1 to i*i
{ for k = 1 to j
       c. double x, y;
       x = 3.5 ; y = 4.0;
list[i] = x * y;
x = 3.5 * x;
y = y + list[i];
25. Which is more efficient: Linear search or Binary search and why? Explain with example.
26. Give asymptotic analysis of Insertion sort and binary search algorithms.
27. Prove or disprove that best case for insertion sort can be Ω(n). Can it be (n)?
28. How many comparisons are needed in best case of bubble sort?
29. Will it be efficient to use insertion sort for data of let’s say 100000 entries or 100
entries?
30. Trace insertion sort and bubble sort on following data and compare the two algorithms:
25 80 45 20 60 45
Glossary
    1.   Algorithm: A computable set of steps to achieve a desired result.
    2.   Array: An assemblage of items that is randomly accessible by integers, the index.
    3.   Asymptote: A line whose distance to a given curve tends toward zero.
    4.   Asymptotic bound: A curve representing the limit of a function. That is, the distance
         between a function and the curve tends to zero. The function may or may not
         intersect the bounding curve.
    5.   Asymptotically tight bound: When the asymptotic complexity of an algorithm exactly
         matches the theoretically proved asymptotic complexity of the corresponding
         problem. Informally, when an algorithm solves a problem at the theoretical
         minimum.
    6.   Asymptotic lower bound: An asymptotic bound, as function of the size of the input,
         on the best (fastest, least amount of space used, etc.) an algorithm can possibly
         achieve to solve a problem. That is, n o algorithm can use fewer resources than the
         bound.
    7.   Asymptotic Upper bound: An asymptotic bound, as function of the size of the input,
         on the worst (slowest, most amount of space used, etc.) an algorithm will do to
         solve a problem.
    8.   Big-Oh: A theoretical measure of the execution of an algorithm, usually the time or
         memory needed, given the problem size n, which is usually the number of items.
    Informally, saying some equation f(n) = O(g(n)) means it is less than some
    constant multiple of g(n). The notation is read, "f of n is big oh of g of n".
9. Binary search: technique for quickly locating an item in a sequential list. The desired
    key is compared to the data in the middle of a sequential index or in the middle of a
    sequential file. The half that contains the data is then compared in the middle, and
    so on, either until the key is located or a small enough group is isolated to be
    sequentially searched.
10. Bubble Sort: A sorting technique that is typically used for sequencing small lists. It
    starts by comparing the first item to the second, the second to the third and so on
    until it finds one item out of order. It then swaps the two items and starts over. The
    sort may alternate from the top of the list to the bottom and then from the bottom
    to the top. The name comes from the notion that items are raised or "bubbled up"
    to the top.
11. Complexity: The intrinsic minimum amount of resources, for instance, memory,
    time, messages, etc., needed to solve a problem or execute an algorithm.
12. Function: A computation which takes some arguments or inputs and yields an
    output. Any particular input yields the same output every time. More formally, a
    mapping from each element in the domain to an element in the range. (2) A
    subroutine which returns a value.
13. Insertion sort: A simple sorting technique that scans the sorted list, starting at the
    beginning, for the correct insertion point for each of the items from the unsorted
    list. Similar to the way people manually sort items, an insertion sort is not efficient
    for large lists, but is relatively fast for adding a small number of new items
    periodically.
14. Linear: Sequential or having a graph that is a straight line.
15. Little –o : A theoretical measure of the execution of an algorithm, usually the time
    or memory needed, given the problem size n, which is usually the number of items.
    Informally, saying some equation f(n) = o(g(n)) means f(n) becomes insignificant
    relative to g(n) as n approaches infinity. The notation is read, "f of n is little oh of g
    of n".
16. Little –omega : A theoretical measure of the execution of an algorithm, usually the
    time or memory needed, given the problem size n, which is usually the number of
    items. Informally, saying some equation f(n) = ω (g(n)) means g(n) becomes
    insignificant relative to f(n) as n goes to infinity.
17. Monotonicity: Mathematics designating sequences, the successive members of which
    either consistently increase or decrease but do not oscillate in relative value. Each
    member of a monotone increasing sequence is greater than or equal to the
    preceding member; each member of a monotone decreasing sequence is less than
    or equal to the preceding member.
18. Notation: Any series of signs or symbols used to represent quantities or elements in
    a specialized system, such as music or mathematics.
19. Omega: The Greek letter written as ω (n) or Ω (n). A theoretical measure of the
    execution of an algorithm, usually the time or memory needed, given the problem
    size n, which is usually the number of items. Informally, saying some equation f(n)
    = Ω (g(n)) means it is more than some constant multiple of g(n).
20. Reflexive: Logic (of a relation) always holding between a term and itself.
21. Running Time: The execution time of an algorithm.
    22. Theta: A theoretical measure of the execution of an algorithm, usually the time or
        memory needed, given the problem size n, which is usually the number of items.
        Informally, saying some equation f(n) = Θ (g(n)) means it is within a constant
        multiple of g(n). The equation is read, "f of n is theta g of n".
    23. Time Complexity: The limiting behavior of the execution time of an algorithm when
        the size of the problem goes to infinity.
    24. Transitive: A binary relation R for which a R b and b R c implies a R c.
    25. Search: To look for specific data in a file or an occurrence of text in a file. A search
        implies sequential scanning of content or indexes in order to find the results rather
        than a direct lookup. A search on the Web yields a list of Web pages that contain all
        the words in the search criteria.
    26. Space Complexity: The limiting behavior of the use of memory space of an
        algorithm when the size of the problem goes to infinity.
References
Suggested Readings
1. Kenneth Rosen, Discrete Mathematics and Its Applications, Sixth Edition
Web Links
http://en.wikipedia.org/wiki/
http://www.itl.nist.gov/div897/sqg/dads/#A
http://www.pcmag.com/encyclopedia/
http://www.thefreedictionary.com/notation