Analyzing recursive algorithms

  • What is the running time of the following recursive method:

       public static void recurse(int n)
       {
           if ( n == 0 )
              doPrimitive();
           else
           {
    	  for ( int i = 0; i < n; i++ )
                  doPrimitive();
              recurse(n-1);
           }
       } 

    Analysis:

        How many times will doPrimitive() be executed 
        when input of the recurse method is n ?
    
    
        The analysis of recursive algorithms
        require the use of:
    
             recurrence relations....
    

Analyzing recursive algorithms

  • What is the running time of the following recursive method:

       public static void recurse(int n)
       {
           if ( n == 0 )
              doPrimitive();
           else
           {
    	  for ( int i = 0; i < n; i++ )
                  doPrimitive();
              recurse(n-1);
           }
       } 

    Assume: C(n) = # times that doPrimitive() is executed when recurse() is called with n:

     Let C(n) = # times that doPrimitive() is executed when input = n
    
    
    
    
    
    
    
    

Analyzing recursive algorithms

  • What is the running time of the following recursive method:

       public static void recurse(int n)
       {
           if ( n == 0 )
              doPrimitive();
           else
           {
    	  for ( int i = 0; i < n; i++ )
                  doPrimitive();
              recurse(n-1);
           }
       } 

    C(0) = 1 because recurse() will execute doPrimitive() one time and terminate:

     Let C(n) = # times that doPrimitive() is executed when input = n
    
     C(0) = 1    // It's the base case
    
    
    
    
    
    

Analyzing recursive algorithms

  • What is the running time of the following recursive method:

       public static void recurse(int n)
       {
           if ( n == 0 )
              doPrimitive();
           else
           {
    	  for ( int i = 0; i < n; i++ )
                  doPrimitive();
              recurse(n-1);
           }
       } 

    When n > 0, recurse(n) will execute doPrimitive() n times and calls recurse(n-1):

     Let C(n) = # times that recurse() is executed when input = n
    
     C(0) = 1    // It's the base case 
     C(n) = n + C(n-1)     for n > 0
         because:
                recurse(n) will invoke recurse(n-1)
                and by definition, the # times that doPrimitive()
                is executed when input = n-1 is: C(n-1)
    

Analyzing recursive algorithms

  • Therefore:   the recurrence relation that represents the running time is:

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
    
    
       We now proceed to solve this recurrence relation...
    
       The commonly used technique is "telescoping"
       I found a video on YouTube on this technique: click here
    
       I will show you this "telescoping" technique
       in the next few slides...
    
    
    
    
    
    
    
    

Analyzing recursive algorithms

  • Solving the recurrence relation: with "telescoping":

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
     C(n) = n + C(n-1)    // We start with the general recurrence relation
                          // and reduce it to the base case
      
    
    
    
    
    
    
    
      
    
     
    
    
    
    

Analyzing recursive algorithms

  • We can use C(n) = n + C(n-1) to obtain a formula for C(n-1) by substituting n ==> n-1:

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
     C(n) = n + C(n-1) ==> C(n-1) = (n-1) + C((n-1)-1) = (n-1) + C(n-2)
    
      
    
      
    
    
    
    
    
    
    
     
    
    
    
    

Analyzing recursive algorithms

  • Substitute the expression for C(n-1) in the formula for C(n):

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
     C(n) = n + C(n-1) ==> C(n-1) = (n-1) + C((n-1)-1) = (n-1) + C(n-2)
          = n + (n-1) + C(n-2)
    
    
      
    
    
    
    
    
    
    
     
    
    
    
    

Analyzing recursive algorithms

  • We can use C(n) = 1 + C(n-1) to obtain a formula for C(n-2) by substituting n ==> n-2:

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
     C(n) = n + C(n-1) ==> C(n-2) = (n-2) + C((n-2)-1) = (n-1) + C(n-3)
          = n + (n-1) + C(n-2)
    
    
      
    
      
     
    
    
    
    
    
    
    
    
    

Analyzing recursive algorithms

  • Substitute the expression for C(n-2) in the formula for C(n):

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
     C(n) = n + C(n-1) ==> C(n-2) = (n-2) + C((n-2)-1) = (n-2) + C(n-3)
          = n + (n-1) + C(n-2)
          = n + (n-1) + (n-2) + C(n-3)
    
      
     
    
    
    
    
    
    
    
    
    
    
    

Analyzing recursive algorithms

  • We can see a pattern and can predict the next progression:

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
     C(n) = n + C(n-1) ==> C(n-3) = (n-3) + C((n-3)-1) = (n-3) + C(n-4)
          = n + (n-1) + C(n-2)
          = n + (n-1) + (n-2) + C(n-3)
          = n + (n-1) + (n-2) + (n-3) + C(n-4)
    
          
      (1) gets an extra   (n-3)  term
      (2) C(n-3) becomes C(n-3-1)
    
      (If you don't see the pattern, substitute C(n-3) and work it out)
    
    
    
    
    
    
    

Analyzing recursive algorithms

  • Repeat these steps n times and we will get C(0):

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
     C(n) = n + C(n-1) ==> C(n-3) = (n-3) + C((n-3)-1) = (n-3) + C(n-4)
          = n + (n-1) + C(n-2)
          = n + (n-1) + (n-2) + C(n-3)
          = ....
          = n + (n-1) + (n-2) + ... + 3 + 2 + 1 + C(0)
           <---------------------------------->
                  triangular sum !
    
    
    
    
    
    
    
    
    
    

Analyzing recursive algorithms

  • Substitute C(0) = 1 and obtain the final result:

     Let C(n) = # times that recurse() is executed when input = n
    
     C(n) = n + C(n-1)     for n > 0,   and  C(0) = 1  
    
     C(n) = n + C(n-1) ==> C(n-3) = (n-3) + C((n-3)-1) = (n-3) + C(n-4)
          = n + (n-1) + C(n-2)
          = n + (n-1) + (n-2) + C(n-3)
          = ....
          = n + (n-1) + (n-2) + ... + 3 + 2 + 1 + 1
           <---------------------------------->
                  triangular sum !
    
          = n(n+1)/2 + 1    ==>  Runtime complexity = O(n2)
    
    
    
    
    
    
    
    

DEMO: demo/13-analysis/Demo10.java