Difference between revisions of "CSC111 Lab 13"

From dftwiki3
Jump to: navigation, search
(Binary Search)
 
(20 intermediate revisions by the same user not shown)
Line 15: Line 15:
  
 
<source lang="python">
 
<source lang="python">
# factorial.py
+
# lab12_1.py
# Demonstrates a recursive factorial function
+
# A simple program that uses a recursive function to compute
 +
# the factorial of a number.
  
 
def fact( n ):
 
def fact( n ):
 +
    """fact: receives an integer, and computes its factorial recursively"""
 
     if n<=1:  
 
     if n<=1:  
 
         return 1
 
         return 1
 +
 
     y = fact( n-1 )
 
     y = fact( n-1 )
 
     result = n * y
 
     result = n * y
Line 27: Line 30:
  
 
def main():
 
def main():
     n = input( "Enter a positive number: " )
+
    """Inputs a number, outputs its factorial"""
 +
     n = int( input( "Enter a positive number: " ) )
 
     x = fact( n )
 
     x = fact( n )
     print "the factorial of %d is %d." % ( n , x )
+
     print( "the factorial of {0:1} is {1:1}".format( n , x ) )
  
 
main()
 
main()
Line 35: Line 39:
 
</source>
 
</source>
 
<br />
 
<br />
Run your program and verify that it computes the correct result.  Below are some factorials numbers to compare your output to.
+
* Run your program  
 +
* It will prompt you for a number and will return its factorial.
 +
* Verify that it computes the correct result.  Below are some factorials numbers to compare your output to.
  
 
   1! =                    1
 
   1! =                    1
Line 54: Line 60:
 
;Question 1
 
;Question 1
 
:
 
:
:*Add print statements to your fact function so that it will print exactly what it does.  For example, before it tests to see if ''n'' is less than equal to 1, you could print:
+
:*Add print statements to your '''fact()''' function so that it will print exactly what it does.  For example, before it tests to see if ''n'' is less than equal to 1, you could print:
  
     print "fact function started.  Receives n =", n
+
     print("fact function started.  Receives n =", n )
     print "testing if n is >= 1"
+
     print( "testing if", n, "is >= 1" )
  
:*Add print statements that will show the values of y and result.
+
:*Add print statements that will show the values of '''y''' and '''result'''.
  
:*Run your program and observe the print statementsCan you see better how ''fact'' recursively calls itself?
+
:*Run your program and observe its outputYou should be able to better observe how the function ''fact()'' recursively calls itself?
  
 
;Question 2
 
;Question 2
 
:
 
:
:*Create the more sophisticated program shown below.  Observe it well and try to figure out what the '''indent''' variable does.
+
:*Create the more sophisticated program shown below.  Observe it well first, and try to figure out what the '''indent''' variable does.
  
 
<br />
 
<br />
Line 100: Line 106:
 
:*Run the program
 
:*Run the program
 
:*Explain the pattern made by the printed lines.  Why this shape?
 
:*Explain the pattern made by the printed lines.  Why this shape?
:*Where does  the stopping condition appear in the printed lines?
+
:*Where does  the stopping condition appear in the printed lines? In other words, where is the printed statement that indicates that '''fact()''' just received a value of ''n'' equal to 1?  Why isn't this statement at the end of the printout?
 +
 
  
 
==Exercise: Write a recursive function==
 
==Exercise: Write a recursive function==
  
 
* Write a program with a recursive function.  The function returns the largest element of a list using the following formula:
 
* Write a program with a recursive function.  The function returns the largest element of a list using the following formula:
 
+
<br />
 +
<br />
 +
::<source lang="text">
 
     largest( A )  = A[0]                                  if len( A ) == 1
 
     largest( A )  = A[0]                                  if len( A ) == 1
 
      
 
      
 
                   = max( A[0], largest( A[1:] ) )          otherwise
 
                   = max( A[0], largest( A[1:] ) )          otherwise
 +
 +
</source>
 +
<br />
 +
<br />
  
 
* Test your program on different Arrays of varying sizes.  We assume that the arrays will always have at least one element.
 
* Test your program on different Arrays of varying sizes.  We assume that the arrays will always have at least one element.
 +
 +
<br />
 +
<br />
 +
 +
* ''' ''Hints'' '''
 +
*# the function definition is simply  '''def largest( A ):'''
 +
*# write the stopping condition first ('''if len(A)...''')
 +
*# if the stopping condition is not met, compute the '''max()''' of '''A[0]''' and '''largest( ''A minus the first element'' )'''
 +
  
 
<font color="white"><tt>def largest( A ):<br />&nbsp;&nbsp;&nbsp;if len( A )==1:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return A[0]<br />&nbsp;&nbsp;&nbsp;return max( A[0], largest( A[1:] ) )
 
<font color="white"><tt>def largest( A ):<br />&nbsp;&nbsp;&nbsp;if len( A )==1:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return A[0]<br />&nbsp;&nbsp;&nbsp;return max( A[0], largest( A[1:] ) )
 
</tt></font>
 
</tt></font>
 
  
 
==Exercise: A more sophisticated recursive function==
 
==Exercise: A more sophisticated recursive function==
  
 
* This time the largest is defined as the max of the largest of the left half of the array, and of the largest of the right half of the array.
 
* This time the largest is defined as the max of the largest of the left half of the array, and of the largest of the right half of the array.
 +
<br />
 +
<br />
 +
<source lang="text">
  
 
     largest( A )  = A[0]                                                  if len( A ) == 1
 
     largest( A )  = A[0]                                                  if len( A ) == 1
 
      
 
      
 
                   = max( largest( A[0:N/2], largest( A[N/2:] ) )          otherwise.  N=len(A)
 
                   = max( largest( A[0:N/2], largest( A[N/2:] ) )          otherwise.  N=len(A)
 +
 +
</source>
 +
<br />
 +
<br />
 +
 +
<font color="white"><tt>def largest( A ):<br>&nbsp;&nbsp;&nbsp;if len(A)==1:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return A[0]<br>&nbsp;&nbsp;&nbsp;return max( largest( A[0:len(A)/2] , largest( A[len(A)/2: ] )</tt></font>
  
 
==Binary Search==
 
==Binary Search==
Line 129: Line 159:
 
* Observe the main function. Make sure you figure out how large the array is.
 
* Observe the main function. Make sure you figure out how large the array is.
  
* Run the program and verify that the search function does a good job locating or not the numbers.
+
* Run the program and verify that the search function does a good job reporting if a number is in the array of not.
 
 
  
 
;First problem
 
;First problem
Line 164: Line 193:
  
 
:*Make sure you reset the counter to 0 for every new search!
 
:*Make sure you reset the counter to 0 for every new search!
 +
 +
;Second question
 +
:
 +
:* Run your program several times for lists of size '''20''', '''100''', '''1,000''', '''10,000''', and '''100,000'''. 
 +
:* For each list size, record the number of comparisons it takes to find items
 +
:* What is the relationship between the size of the list and the (average) number of comparisons?
 +
 +
==Optional and for fun: Exploring a Maze==
 +
 +
<br />
 +
<tanbox>
 +
Here is an example of a complex problem that can be (relatively) easily solved using a recursive program.
 +
</tanbox>
 +
<br />
 +
 +
*Get a copy of the [[CSC111 visitMaze.py | maze-visiting program]], and run it.
 +
 +
    getcopy visitMaze.py
 +
 +
*      Observe how the recursive '''visitMaze()''' function leaves "bread crumbs" behind the cells that it visits, and "v" characters in cells visited but known not to lead to an exit.
 +
 +
*      Edit the program and switch the order in which the function visits cells in the 4 directions. Make the recursive function decide to go up first, then left, then right, and finally down.
 +
 +
*      Run the program and see how this change influences the way the program explores the maze.
 +
 +
*      Modify your program so that the maze is now a much simpler maze, in the spirit of the maze shown below:
 +
 +
 +
      mazeText = """
 +
      #########################
 +
      ........................#
 +
      #.......................#
 +
      #.......................#
 +
      #...........#...........#
 +
      #...........#...........#
 +
      #############...........#
 +
      #...........#...........#
 +
      #.......................#
 +
      #........################
 +
      #........................
 +
      #.......................#
 +
      #.......................#
 +
      #########################
 +
      """
 +
 +
 +
*      Predict the path that the program is going to take when visiting it.
 +
 +
*      Repeat the same experiment as before and change the order in which the recursive function visits the different directions, and observe if this makes the function find the exit faster.
 +
 +
===    Treasures in the maze===
 +
 +
*      Let's change the rules of the game, and make your program find a path to a treasure, rather than a path to an exit. In our case the treasure will be a special character, say '&', inside the maze.
 +
 +
*      Modify your the visitMaze() function so that it now returns true if it finds the treasure, and not an exit. Verify that your program displays at the end the path to the treasure.
 +
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
[[Category:CSC111]][[Category:Python]][[Category:Labs]]

Latest revision as of 07:00, 19 April 2015

--D. Thiebaut 15:09, 28 April 2010 (UTC)


This lab deals with recursive functions.

Factorial

Create a copy of this simple example:

# lab12_1.py
# A simple program that uses a recursive function to compute
# the factorial of a number.

def fact( n ):
    """fact: receives an integer, and computes its factorial recursively"""
    if n<=1: 
        return 1

    y = fact( n-1 )
    result = n * y
    return result


def main():
    """Inputs a number, outputs its factorial"""
    n = int( input( "Enter a positive number: " ) )
    x = fact( n )
    print( "the factorial of {0:1} is {1:1}".format( n , x ) )

main()


  • Run your program
  • It will prompt you for a number and will return its factorial.
  • Verify that it computes the correct result. Below are some factorials numbers to compare your output to.
  1! =                    1
  2! =                    2
  3! =                    6
  4! =                   24
  5! =                  120
  6! =                  720
  7! =                 5040
  8! =                40320
  9! =               362880
 10! =              3628800
 11! =             39916800
 12! =            479001600
 13! =           6227020800
 14! =          87178291200
Question 1
  • Add print statements to your fact() function so that it will print exactly what it does. For example, before it tests to see if n is less than equal to 1, you could print:
    print("fact function started.  Receives n =", n )
    print( "testing if", n, "is >= 1" )
  • Add print statements that will show the values of y and result.
  • Run your program and observe its output. You should be able to better observe how the function fact() recursively calls itself?
Question 2
  • Create the more sophisticated program shown below. Observe it well first, and try to figure out what the indent variable does.


# factorialPrint.py
# Demonstrates a recursive factorial function
 
def fact( n, indent ):
    print indent, "fact(%d) started" % n
    print indent, "comparing %d to 1" % n
    if n<=1: 
        print indent, "%d is <= 1.  Returning 1" % 1
        return 1

    print indent, "%d is not <= 1.  Calling fact(%d) and storing it in y" % (n, n-1)
    y = fact( n-1, indent + "    "  )
    print indent, "just received %d from fact( %d )." % ( y, n-1 )
    result = n * y
    print indent, "multiplied %d by %d.  It's %d.  Returning %d to caller" % ( n, y, result, result )
    return result
 
 
def main():
    # print first 15 factorials 
    n = input( "Enter a positive integer: " )
    print "Main calls fact( %d )" % n 
    y = fact( n, "   " )
    print "Main receives result = ", y
 
main()
  • Run the program
  • Explain the pattern made by the printed lines. Why this shape?
  • Where does the stopping condition appear in the printed lines? In other words, where is the printed statement that indicates that fact() just received a value of n equal to 1? Why isn't this statement at the end of the printout?


Exercise: Write a recursive function

  • Write a program with a recursive function. The function returns the largest element of a list using the following formula:



    largest( A )  = A[0]                                   if len( A ) == 1
    
                  = max( A[0], largest( A[1:] ) )          otherwise



  • Test your program on different Arrays of varying sizes. We assume that the arrays will always have at least one element.



  • Hints
    1. the function definition is simply def largest( A ):
    2. write the stopping condition first (if len(A)...)
    3. if the stopping condition is not met, compute the max() of A[0] and largest( A minus the first element )


def largest( A ):
   if len( A )==1:
      return A[0]
   return max( A[0], largest( A[1:] ) )

Exercise: A more sophisticated recursive function

  • This time the largest is defined as the max of the largest of the left half of the array, and of the largest of the right half of the array.



    largest( A )  = A[0]                                                   if len( A ) == 1
    
                  = max( largest( A[0:N/2], largest( A[N/2:] ) )          otherwise.  N=len(A)



def largest( A ):
   if len(A)==1:
      return A[0]
   return max( largest( A[0:len(A)/2] , largest( A[len(A)/2: ] )

Binary Search

  • Create a copy of the binary search program we saw in class yesterday.
  • Observe the main function. Make sure you figure out how large the array is.
  • Run the program and verify that the search function does a good job reporting if a number is in the array of not.
First problem
  • Modify the program so that it counts the number of comparisons that it performs. A good way to do this is to create a global variable, we'll call it count, that is declared at the beginning of your program, and which you increment by one, every time the binsearch function compares two quantities. These comparisons are a good indicator of the amount of "work" done by the function, since it is obvious just looking at the program to figure out how much time the function will call itself.
  • The correct way to use count as a global variables is illustrated below. You initialize the variable at the beginning of the program, and in the functions that need to use it, you define count as global so that python will know that count is not just a local variable used inside the function, but refers to something defined outside the function.


# program header
# 
count = 0
 
def binsearch( ... ):
    global count

    ...
    count += 1


def main():
    global count
    
    ...
    count = 0
    binarySearch( ... )
    ...
  • Make the main program print the number of comparisons performed after the function returns to main.
  • Make sure you reset the counter to 0 for every new search!
Second question
  • Run your program several times for lists of size 20, 100, 1,000, 10,000, and 100,000.
  • For each list size, record the number of comparisons it takes to find items
  • What is the relationship between the size of the list and the (average) number of comparisons?

Optional and for fun: Exploring a Maze


Here is an example of a complex problem that can be (relatively) easily solved using a recursive program.


    getcopy visitMaze.py
  • Observe how the recursive visitMaze() function leaves "bread crumbs" behind the cells that it visits, and "v" characters in cells visited but known not to lead to an exit.
  • Edit the program and switch the order in which the function visits cells in the 4 directions. Make the recursive function decide to go up first, then left, then right, and finally down.
  • Run the program and see how this change influences the way the program explores the maze.
  • Modify your program so that the maze is now a much simpler maze, in the spirit of the maze shown below:


     mazeText = """
     #########################
     ........................#
     #.......................#
     #.......................#
     #...........#...........#
     #...........#...........#
     #############...........#
     #...........#...........#
     #.......................#
     #........################
     #........................
     #.......................#
     #.......................#
     #########################
     """


  • Predict the path that the program is going to take when visiting it.
  • Repeat the same experiment as before and change the order in which the recursive function visits the different directions, and observe if this makes the function find the exit faster.

Treasures in the maze

  • Let's change the rules of the game, and make your program find a path to a treasure, rather than a path to an exit. In our case the treasure will be a special character, say '&', inside the maze.
  • Modify your the visitMaze() function so that it now returns true if it finds the treasure, and not an exit. Verify that your program displays at the end the path to the treasure.