CSC300 May09

slide version

single file version

Contents

  1. Logarithm Definition
  2. Basic Property of Log
  3. Derived Properties of the logaritm
  4. Log and tilde approximations
  5. Useful Identities for Program Run Time Analysis
  6. Running Time
  7. Model for the Examples
  8. Examples
  9. Example 1
  10. Example 2
  11. Example 3
  12. Example 4
  13. Example 5
  14. Example 6
  15. Problem
  16. Doubly Linked Nodes
  17. Boundary Cases
  18. Head and Tail Guard Nodes
  19. Homework
  20. Running Time Problems
  21. Program Problem

Logarithm Definition[1] [top]

Definition

The log of N to the base b is the exponent a such that

      N = ba
    

and is written

      logb(N) = a
    

For example,

      1000 = 103,  so log10(1000) = 3

      1024 = 210,  so log2(1024) = 10
    

Logarithms with different Bases

Log to the base 2 is just a constant multiple of the log to the base 10.

Log to base 2 is a (different) constant multiple of the log to any other base b, where the constant depends only on the two bases.

      log2(N) = log10(N) / log10(2)
    

For example, what if N is 2?

Basic Property of Log[2] [top]

Since logarithms of different bases differ only by a constant depending only on the bases, the base will be omitted and assumed to be 2 unless otherwise noted.

The basic property of logarithm function (for any base):

 log(xy) = log(x) + log(y)
    
      For 
      x = 2a,   log(x) = a
      y = 2b,   log(y) = b

      xy = 2a + b
    

So

      log(xy) = a + b = log(x) + log(y)
    

Derived Properties of the logaritm[3] [top]

 (1) log(2N) = log(N) + 1

 (2) log(N2) = 2log(N)

 (3) log(N100) = 100log(N)

 (4) log(N0.5) = 0.5log(N)
    

Log and tilde approximations[4] [top]

Since

      2N + 1 ~ 2N
    

this can be combined with the logarithm and then use the log properties to get a tilde approximation for

      log(2N + 1) ~ log(N)
    

like this:

(1) log(2N + 1) ~ log(2N) 

(2) log(2N) = log(2) + log(N) = 1 + log(N)

(3) 1 + log(N) ~ log(N)
    

Useful Identities for Program Run Time Analysis[5] [top]

O(...) means "order of growth ...

Arithmetic Sum


(1)   1 + 2 + 3 + 4 + ... + N = 0.5N2 + 0.5N 
                              ~ 0.5N2
                              = O(N2)
    

Geometric Sum

For N a power of 2: N = 2k


(2) 1 + 2 + 4 + 8 + ... + N = 2N - 1
                            ~ 2N
                            = O(N)
    

Running Time[6] [top]

Suggested steps

  1. Decide on an input model; that is, what will be used as the input size.
  2. Identify the inner loop; that is, the part of the code that will execute most often and/or contribute most to the running time.
  3. Decide on a cost model; that is what operations in the inner loop are to be counted.
  4. Determine the the total count of the operations in the cost model for a given input size.

Model for the Examples[7] [top]

The input model in the following examples will just use the input size as the value of N.

The cost model will be to body of the inner for loop (or the single for loop if there is no nested loop). The only operation to count in the inner loop is the single statement:

 sum++;      
    

That is, this will count as 1 operation.

This model seems to make many simplifications, but as seen previously it likely will yield the same tilde approximation and order of growth as a more detailed model.

Examples[8] [top]


      // Example 1 O(N)
    for(int i = 0; i < N; i++) {
      sum++;
    }

      // Example 2 O(N^2)
    for(int i = 1; i <= N; i++) {
      for(int j = 0; j < N; j++) {
	sum++;
      }
    }

      // Example 3 O(N^2)
    for(int i = 1; i <= N; i++) {
      for(int j = 0; j < i; j++) {
	sum++;
      }
    }

      // Example 4 O(N)
    for(int i = 1; i < N; i *= 2) {
      for(int j = 0; j < i; j++) {
	sum++;
      }
    }

      // Example 5 O( Nlog(N) )
    for(int i = 1; i < N; i *= 2) {
      for(int j = 0; j < N; j++) {
	sum++;
      }
    }

      // Example 6 O( log(N) )
    for(int i = N; i > 1; i /= 2) {
      sum++;
    }

Example 1[9] [top]

    for(int i = 0; i < N; i++) {
      sum++;
    }
 
    

Example 2[10] [top]

    for(int i = 1; i <= N; i++) {
      for(int j = 0; j < N; j++) {
	sum++;
      }
    }
 
    

Example 3[11] [top]

    for(int i = 1; i <= N; i++) {
      for(int j = 0; j < i; j++) {
	sum++;
      }
    }
 
    

Example 4[12] [top]

Assume N is a power of 2; e.g., N = 2k

    for(int i = 1; i < N; i *= 2) {
      for(int j = 0; j < i; j++) {
	sum++;
      }
    }
 
    

Example 5[13] [top]

Assume N is a power of 2; e.g., N = 2k

    for(int i = 1; i < N; i *= 2) {
      for(int j = 0; j < N; j++) {
	sum++;
      }
    }
 
    

Example 6[14] [top]

Assume N is a power of 2; e.g., N = 2k

    for(int i = N; i > 1; i /= 2) {
      sum++;
    }
 
    

Problem[15] [top]

A machine executes 1 instruction per microsecond (that is, 1,000,000 instructions per second).

Algorithm k (for k = 1, 2, 3, 4) takes Tk(N) microseconds for input of size N.

What is the largest input size, N, that each algorithm can complete in 1 second?

      T1(N) = log(N)  (base 2)
      T2(N) = N
      T3(N) = N2
      T4(N) = N3
    

Doubly Linked Nodes[16] [top]


private class Node
{
  public E data;  /* E declared in the containing class */
  public Node next;
  public Node prev;

  public Node(E d)
  {
    data = d;
    next = null;
    prev = null;
  }
}

Boundary Cases[17] [top]

Using doubly linked nodes can solve the problem of removing the last element efficiently.

Using doubly linked nodes does a bit more code to update both next and prev members instead of just next.

But both doubly linked lists as well as singly linked lists have to be careful to handle boundary cases:

One addition to the structure will allow these boundary cases to be handled with the same code as non boundary cases.

The change is to add two guard nodes: one at the beginning before the first data node and the other after the last data node.

This has the benefit that every data node will have a node before it and one after. In particular this is true even for the first data node and for the last data node.

Head and Tail Guard Nodes[18] [top]

The head and tail nodes below are the guard nodes:

Empty List: size 0

Non-Empty List: size 3

Homework[19] [top]

Implement a generic Deque class that implements Iterable.

Deque<E>
-Node head;
-Node tail
-int sz;
Deque()
boolean isEmpty()
int size()
void pushLeft(E x)
void pushRight(E x)
E popLeft()
E popRight()
Iterator<E> iterator()

Running Time Problems[20] [top]

  1. ThreeSum
  2. Closest Pair
  3. Farthest Pair
  4. Autoboxing
  5. Estimating the Running time

Program Problem[21] [top]

Implementing a Queue using two Stacks