Lecture 6:  Time Complexity Analysis of Program Efficiency

 

How to determine complexity of different code structures

It depends on what kinds of statements are in the code

1                                            Sequence of statements:  s1; s2; s3; … sk;

Time for whole seq = sum of times for each statement

If each stmt only involves a fixed number of basic ops,

        Then each is O(1)

        Total time is also O(1)

 

2      If – then – else statements:

       if (cond) {

       then   {

         Seq of stmts 1

        }

       else  {

         Seq of Stmts 2

       }

          executes either seq1 or seq 2

         worst case time is:

                max (time (seq1), time (seq 2))  +  time (cond)    

      

       test yourself:

Time(cond)

Time(seq1)

Time(seq2)

Total Time

O(1)

O(1)

O(1)

O(1)

O(1)

O(N)

O(1)

O(N)

O(1)

O(N)

O(N)

O(N)

O(N)

O(N)

O(1)

O(N)

O(N)

O(N)

O(N)

O(N)

O(1)

O(N2)

O(1)

O(N2)

 

       if you finish early, check with your neighbor,

                                        make some up for your partner

 

 

3      Loops

For (k=0; k < N; k++) {

  Seq of stmts

}

loop executes N times; seq of stmts executes N times

total time = N ´ time for seq of statements:  

     time for stmts:   O(1)        O(N)         O(M)

             total time:           O(N)      O(N2)        O(N´M)

 

4      Nested Loops

case #1:  number of iterations of inner loop is

                    independent of outer loop index

for (j=0; j < N; j++) {

    seq1 of stmts

    for (k=0; k < M; k++) {

       Seq2 of stmts

    }

}

·       outer loop executes N times

·       each time, inner loop executes M times

·       seq of stmts executes N´M times

total time = N ´ (time(seq1) + M ´ time(seq2))      

time for stmts:     O(1)        O(N)         O(M)        O(P)

        total time:   O(N´M)    O(N2M)     O(NM2)    O(NMP)

case #2:  number of iterations of inner loop

                    depends on outer loop index

for (j=0; j < N; j++) {

    for (k=j; k < N; k++) {

       Seq of stmts

    }

}

·       outer loop executes N times

·       inner loop executes N, N-1, N-2, …, 2, 1

·       seq of stmts executes N(N+1)/2 times

total time = N ´ M ´ time for seq of statements:   

time for stmts:     O(1)        O(N)       O(M)  

        total time:     O(N2)     O(N3)      O(N2M)

 

Statements with method calls:

example:   Object k = L.get(1);

    time:  get is O(1), assignment is O(1),  total is O(1)

·       time for statement includes the time for the get method

·       time for get may depend on value of parameter, fields

 

 

Read Announcements HERE

Handout:  what is the complexity of each block of code?

 

Example 1

        Object ob = L.get ( L.size() );

 

Example 2

Object ob = L.remove(0);

 

Example 3

for (int k=0; k < L.size(); k++) {

    System.out.println(L.get(k));

}

 

Example 4

for (int k=L.size(); k>0; k--) {

    L.remove(k));

}

 

Example 5

for (int k=L.size(); k>0; k--) {

    L.remove(1));

}

Solutions:

Example 1:          O(1)

Examples 2,3,4:  O(N)

Example 5:         O(N2)

 

Summary:

·       goal is to determine how running time changes with the problem size, N

·       time ~ number of operations

·       use "Big-O" notation

        ignore constants & lower order terms

 

be able to analyze code to find:

            what is the problem size?

            what is the worst case time?

            how to express it using Big-O notation

 

some typical complexities:

    O(1)         constant    time doesn't change when N doubles

    O(N)        linear         time doubles                "    "      "

    O(log N)   logarithmic   time goes up by 1      "    "      "

    O(N2)      quadratic       time quadruples        "    "      "

    O(N3)      cubic

    O(2N)      exponential    time doubles when N goes up by 1

Review of Java types  (not needed?)

·       primitive types:  int, double, char, …

·       reference types:  arrays, objects defined by classes

5

 

k

 
examples:

int k=5;

5

 

j

 
int j=k;  // j has a copy of value of k

 

A

 
int [] A,B;

B

 
A = new int[3];

B = A;   // B has copy value of A

 

B[1] = 5;  //changes A[1] also – called "aliasing"

B = null;  //changes B itself; doesn't change A

L

 
 


List L; // contents of L = ??

L = new List(); //items ¹ ?? --why?  look at constructor

 

L

 
 

 

 

 

 


aliasing happens when ref. is passed as an argument in a method call;  e.g., L.add(ob);

  (discussed in lecture 2)

Review:  Boolean expressions

In Java,

·       boolean expressions are evaluated left to right

·       evaluation stops when result is known

called "Boolean short-circuiting"

 

example:

        if (pos != k && get(k).equals(get(pos)))

    if pos = k, the second expression is not evaluated

 

thus,

·       put the less expensive expression first

 

similarly,

boolean result = false;

for (k=0; k < numItems; k++){

    if (pos != k && get(pos).equals(get(k)))

         result = true;  // can return true here

}

return result;