1 / 67

# Introduction to Computer Science I Topic 6: Generative Recursion - PowerPoint PPT Presentation

Introduction to Computer Science I Topic 6: Generative Recursion. Prof. Dr. Max Mühlhäuser Dr. Guido Rößling. Outline. Introduction to generative recursion Sorting with generative recursion Guidelines for the design of generative recursive procedures Structural versus generative recursion

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.

Introduction to Computer Science I Topic 6: Generative Recursion

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

## Introduction to Computer Science ITopic 6: Generative Recursion

Prof. Dr. Max Mühlhäuser

Dr. Guido Rößling

### Outline

• Introduction to generative recursion

• Sorting with generative recursion

• Guidelines for the design of generative recursive procedures

• Structural versus generative recursion

• Backtracking: Traversing graphs

### Generative Recursion

• So far, we have used structural recursion to process structurally recursive data

• We have decomposed the data to their immediate structural components

• We have processed these components and combined the results

• However:

• Not all problems can be solved using structurally recursive functions

• Even if there is a structurally recursive solution for a problem, it might not be optimal

• Now we will consider a new programming style: Generative recursion

### Generative Recursion

• Divide and Conqueris an important principle underlying generative recursion

• If the problem is trivial to solve, the solution for the trivial case is returned

• Otherwise:

• Divide the problem in new smaller problems (generate new sub-problems)

• Conquer the sub-problems by applying the same technique recursively

• Combine the solutions for the sub-problems into a solution for the original problem

• The design of generative recursive programs is more an ad-hoc activity as compared to the design of the structural recursive programs that needs an insight – a “Eureka!"

### Modeling a rolling ball on a table

• The ball rolls at a constant speed until it drops off the edge of the table

• We can model the table as a canvas/surface with a pre-defined length and width

• The ball can be represented as a disc that moves across the canvas

• The disc movement can be represented by repeating the following steps:

• Draw the disc at the current position on the canvas

• Wait for a certain pre-defined time

• Erase the disc at the current position

• Move it to a new position

### Ball Structure and Operations

;;TeachPack: draw.ss

;; structure: (make-ball numbernumbernumbernumber)

(define-structball (x y delta-x delta-y))

;; draw-and-clear : a-ball -> true

(define (draw-and-clear a-ball)

(and

(draw-solid-disk (make-posn

(ball-x a-ball)

(ball-y a-ball)) 5 'red)

(sleep-for-a-while DELAY)

(clear-solid-disk (make-posn

(ball-x a-ball)

(ball-y a-ball)) 5 'red)))

### Ball Structure and Operations

;; move-ball : ball -> ball

(define (move-ball a-ball)

(make-ball (+ (ball-x a-ball) (ball-delta-x a-ball))

(+ (ball-y a-ball) (ball-delta-y a-ball))

(ball-delta-x a-ball)

(ball-delta-y a-ball)))

;; Dimension ofsurface

(defineWIDTH100)

(defineHEIGHT100)

;; Delay constant

(defineDELAY.1)

To move the ball multiple times we can write:

This gets tedious after a while.

We need a function that moves the ball until it is out of bounds.

### Ball Structure and Operations

(definethe-ball (make-ball10 20 -5 +17))

(and

(draw-and-clear the-ball)

(and

(draw-and-clear (move-ball the-ball))

...))

### Rolling the Ball

Determine whether a-ball is outside of the bounds:

Template for moving the ball until it is out of bounds:

;; out-of-bounds? : a-ball -> boolean

(define (out-of-bounds? a-ball)

(not

(and

(<=0 (ball-x a-ball) WIDTH)

(<=0 (ball-y a-ball) HEIGHT))))

The trivial case: returntrue

;; move-until-out : a-ball -> true

(define (move-until-out a-ball)

(cond

[(out-of-bounds? a-ball) ... ]

[else...]))

true

?

### Rolling the Ball

After drawing and moving the ball, we apply move-until-outagain, which means the function is recursive:

(define (move-until-out a-ball)

(cond

[(out-of-bounds? a-ball) true]

[else (and (draw-and-clear a-ball)

(move-until-out

(move-ball a-ball)))]))

We can now test the function as follows: The code creates a canvas of proper size and a ball that moves to the bottom left of the canvas.

(startWIDTHHEIGHT)

(move-until-out (make-ball1020-5+17))

(stop)

### A New Type of Recursion

• The procedure move-until-out uses a new type of recursion:

• Conditions have nothing to do with the input data

• Recursive application in the body does not use part of the input

• move-until-outgenerates an entirely new and different ball structure and uses it for the recursion

We do not have a design recipe for this

(define (move-until-out a-ball)

(cond

[(out-of-bounds? a-ball) true]

[else (and (draw-and-clear a-ball)

(move-until-out

(move-ball a-ball)))]))

### Outline

• Introduction to generative recursion

• Sorting with generative recursion

• Guidelines for the design of generative recursive procedures

• Structural versus generative recursion

• Backtracking: Traversing graphs

### Sorting: Quicksort and Mergesort

• We are once more concerned with sorting the elements of a list …

• We have seen insertion sort:

• A structurally recursive procedure

• Now we will see two other algorithms for sorting: Quicksort and Mergesort

• Classic examples of generative recursion

• Based on the idea of “divide and conquer”

### [Reminder: insertionsort]

;; insertion-sort: list-of-numbers  ->  list-of-numbers

;; creates a sortedlistofnumb. fromnumbers in alon

(define (insertion-sortalon)

(cond

[(empty? alon) empty]

[else (insert (firstalon)

(insertion-sort (restalon)))]))

unsorted

sorted

an

an

### Quicksort: The Idea

• An arbitrary intermediate step: sorting an arbitrary sub-list:L0=(list elp…elr)

• Divide: PartitionL0 in two (possibly empty) lists, L1=(list elp…elq-1) and L2=(list elq+1…elr), such that

• each element of L1 is smaller than elq, and

• the latter is in turn smaller than each element in L2

• Conquer: Apply the same procedure recursively to sort L1 and L2

• Combine: simply concatenate the sorted lists L1 and L2

Turningpoint

elq

<= elq

> elq

### Quicksort: The Idea

• Two open questions so far:

• How do we select the pivot element?

• We always use the first element

• When do we stop? That is: what is the trivial case of Quicksort?

• The empty list is always sorted!

### Quicksort: Approach

• Select thefirstelementfromthelistaspivot item

• Determinethefirst sub-listwithelements <= pivot

• Sortthis sub-listrecursivelyusingQuicksort

• Determinethesecond sub-listwithelements > pivot

• Sortthis sub-listrecursivelyusingQuicksort

• Concatenatethesorted sub-liststo a newlist

Sort(list 11 8 7 14):

Select thefirstelementfrom '(11 8 7 14) aspivot item:11

Determinethefirst sub-list <= 11: '(8 7)

Sortthis sub-list

Select thefirstelementfrom '(8 7) aspivot item: 8

Determinethefirst sub-listwithelements <= 8: '(7)

Sortthis sub-list

Select thefirstelementfrom '(7) aspivot item: 7

Determinethefirst sub-listwithelements <= 7: empty

Sortthis sub-list Resultempty

Determinethesecond sub-listwithelements > 7: empty

Sortthis sub-list Resultempty

Concatenate (empty 7 empty) to a newlist -> (list 7)

Determinethesecond sub-listwithelements > 8:  empty

Sortthis sub-list  Resultempty

Concatenate((list 7) 8 empty) to a newlist  (list 7 8)

Determine…

11

(list 14)

(list 8 7)

empty

14

empty

8

empty

(list 7)

empty

7

empty

(list 14)

(list 7)

(list 7 8)

(list 7 8 11 14)

### Quicksortschematically

pivot item

quick-sort distinguishes two cases:

If the input is empty, it returns empty.

Otherwise, it performs recursion.

Each sub-list is sorted separately using quick-sort

The sorted versions of the two lists are then combined using append

### Quicksort Algorithm

;; quicksort2: (listofnumber) -> (listofnumber)

(define (quicksort2 alon)

(cond

[(empty? alon) empty]

[else

(append

(quicksort2 (less-or-equal (restalon)

(firstalon)))

(list (firstalon))

(quicksort2 (greater-than (restalon)

(firstalon))))

]))

### Auxiliary Functions of Quicksort

• greater-than filters out the items that are larger than threshold:

• less-than filters out the items that are smaller than threshold:

(define (greater-than alonthreshold)

(filter1 > alon threshold)))

(define (less-than alonthreshold)

(filter1 < alon threshold)))

### Quicksort Evaluation Example

(quick-sort (list11 8 14 7))

= (append (quick-sort (list8 7))

(list11)

(quick-sort (list14)))

= (append (append (quick-sort (list7))

(list8)

(quick-sortempty))

(list 11)

(quick-sort (list 14)))

= (append (append (append (quick-sortempty)

(list7)

(quick-sortempty))

(list 8)

(quick-sortempty))

(list 11)

(quick-sort (list 14)))

= ...

### Quicksort Evaluation Example

= (append (append (appendempty

(list7)

empty)

(list 8)

empty)

(list 11)

(quick-sort (list 14)))

= (append (append (list7)

(list8)

empty)

(list 11)

(quick-sort (list 14)))

= (append (list78)

(list11)

(quick-sort (list14)))

= ...

### mergesort:The Idea

Idea:

split the list in the middle

apply the function to the sub-lists recursively

merge the two sorted lists into a new sorted list

### Merging two ordered lists

• Given two ordered lists ls1 and ls2

• How do we merge them into a new ordered list?

ls-1

ls-2

1

2

4

7

3

5

6

8

Compareandcopythesmallerelement, thencontinue

sorted-list

1

2

3

4

5

6

7

8

### Merging two ordered lists

(define (merge ls1 ls2)

(cond

[(empty? ls1) ls2]

[(empty? ls2) ls1]

[(<= (first ls1) (first ls2))

(cons (first ls1)

(merge (rest ls1) ls2))

]

[else (cons (first ls2)

(merge ls1 (rest ls2)))]

)

)

### mergesort:algorithm in Scheme

(define (merge-sortalon)

(local

((define (merge-stepleftright)

(cond

[(>= leftright) alon]

[else

(local(

(definemid (floor (/ (+ leftright) 2)))

(defineleft-list

(merge-sort (extractalonleftmid)))

(defineright-list

(merge-sort

(extractalon (+ mid 1) right))))

(mergeleft-listright-list)

)

]

)))

(merge-step 1 (lengthalon))))

### mergesort: algorithm in Scheme

(define (extract alonleft right)

(cond

[(empty? alon) empty]

[(> left right) empty]

[(> left 1)

(extract

(rest alon)

(- left 1)

(- right 1))]

[else (cons

(first alon)

(extract

alon

(+ left 1)

right))]))

### Outline

• Introduction to generative recursion

• Sorting with generative recursion

• Guidelines for the design of generative recursive procedures

• Structural versus generative recursion

• Backtracking: Traversing graphs

### Guidelines for DesigningGenerative Recursive Procedures

• Understand the nature of the data of the procedure

• Describe the process in terms of data, creating a new structure or partitioning a list of numbers

• Distinguish between those input data

• which can be processed trivially,

• and those which cannot

• The generation of problems is the key to algorithm design

• The solutions of the generated problems must be combined

• Data analysis and design:

• Analyze and define data collections representing the problem

• Specify what the function does

• Explain in general terms how it works

• Function examples:

• Illustrate how the algorithm proceeds for some given input

• Template:

• Definition:

• Answer the questions posed by the template

• Test

• Test the completed function

• Eliminate the bugs

### General Template forGenerative Recursive Functions

(define (generative-recursive-funproblem)

(cond

[(trivially-solvable? problem)

(determine-solutionproblem)]

[else

(combine-solutions

... problem ...

(generative-recursive-fun

(generate-problem-1 problem))

...

(generative-recursive-fun

(generate-problem-n problem)))]))

### Procedure Definition

• What is a trivially solvable problem, and the pertaining solution?

• How do we generate new problems that are easier to solve than the initial problem?

• Is there one new problem to generate, or are there more?

• Is the solution for the given problem the same as for (one of) the new problems?

• Or do we have to combine solutions to create a solution for the initial problem?

• If so, do we require parts of the data constituting the initial problem?

### Termination of Structurally Recursive Procedures

• So far, each function has produced an output for a valid input

 Evaluation of structurally recursive procedures has always terminated.

• Important characteristic of the recipe for structurally recursive procedures:

• Each step of natural recursion uses an immediate component of the input, not the input itself

• Because data is constructed in a hierarchical manner, the input shrinks at every stage

• The function sooner or later consumes an atomic piece of data and terminates.

### Termination of Generative Recursive Procedures

• This characteristic is not true for generative recursive functions.

• The internal recursions do not consume an immediate component of the input, but some new piece of data, which is generated from the input.

• This step may produce the initial input over and over again and thus prevent the evaluation from ever producing a result

• We say that the program is trapped in an infinite loop.

### Non-Terminating Procedures

• What happens if we place the following three expressions at the bottom of the DrScheme Definitions window and click execute?

• Does the second expression ever produce a value so that the third expression is evaluated and the canvas disappears?

(startWIDTHHEIGHT)

(move-until-out (make-ball102000))

(stop)

### Non-TerminatingProcedures

The slightest mistake in process definition may cause an infinite loop:

;; less-or-equal: (list-of-numbers) number-> (list-of-numbers)

(define(less-or-equalalonthreshold)

(cond

[(empty?alon) empty]

[else (if (<= (firstalon) threshold)

(cons (firstalon)

(less-or-equalalonthreshold))

(less-or-equal(restalon) threshold))]))

Quicksortdoes not terminatewiththenewfunction

(quick-sort (list 5))

= (append (quicksort (less-or-equal5 (list 5)))

(list 5)

(quicksort (greater-than5 (list 5))))

= (append (quicksort(list 5))

(list 5)

(quicksort (greater-than5 (list 5))))

### Termination Argument

• The termination argument is an additional step in the design recipe of generative recursive procedures

• Termination argument explains

• why the process produces an output for every input

• how the function implements this idea

• when the process possibly does not terminate

### Termination Argument for Quicksort

At each step, quick-sortpartitions the list into two sublists using less-or-equaland greater-than.

Each function produces a list that is smaller than the input (the second argument), even if the pivot element (the first argument) is an item on the list.

Hence, each recursive application of quick-sort consumes a strictly shorter listthan the given one.

Eventually, quick-sortreceives an empty list and returns empty.

### New Termination Cases

Termination argument may reveal additional termination cases.

This knowledge can be added to the algorithm:

So liefert (less-or-equal N (list N))und(greater-than N (list N)) immer empty

;; quick-sort : (listofnumber) -> (listofnumber)

(define (quick-sortalon)

(cond

[(empty? alon) empty]

[(empty? (restalon)) alon]

[else

(append

(quick-sort(less-or-equal (restalon)

(firstalon)))

(list (firstalon))

(quick-sort(greater-thanalon(firstalon))))

]))

### Outline

• Introduction to generative recursion

• Sorting with generative recursion

• Guidelines for the design of generative recursive procedures

• Structural versus generative recursion

• Backtracking: Traversing graphs

trivially-solvable?

empty?

generate-problem

rest

### Structural Recursion as aSpecial Case of Generative Recursion

Template forgenerative recursion

(define (generative-recursive-funproblem)

(cond

[(trivially-solvable? problem)

(determine-solutionproblem)]

[else

(combine-solutions

problem

(generative-recursive-fun

(generate-problemproblem)))]))

Template forlistprocessing

(define (generative-recursive-funproblem)

(cond

[(empty?problem) (determine-solutionproblem)]

[else

(combine-solutions

problem

(generative-recursive-fun (restproblem)))]))

### Structural vs. Generative Recursion

• Is there a difference between structural and generative recursion?

• Structurally recursive functions seem to be just special cases of generative recursion

• But this "it's all the same" attitude does not improve the understanding of the design process

• Structurally and generative recursive functions are designed using different approaches and have different consequences.

### Greatest Common Denominator (GCD)

• Examples

• 6 and 25 are both numbers with several denominators:

• 6 is evenly divisible by 1, 2, 3, and 6;

• 25 is evenly divisible by 1, 5, and 25.

• The greatest common denominator of 25 and 6 is 1.

• 18 and 24 have many common denominators :

• 18 is evenly divisible by 1, 2, 3, 6, 9, and 18;

• 24 is evenly divisible by 1, 2, 3, 4, 6, 8, 12, and 24.

• The greatest common denominator is 6.

### GCD Based on Structural Recursion

Test for every number i = [min(n,m) .. 1] whether it divides both n and m evenly and return the first such number.

;; gcd-structural : N[>= 1] N[>= 1] -> N

(define (gcd-structural n m)

(local

((define (first-divisor i)

(cond

[(= i 1) 1]

[(and(= (remainder n i) 0)

(= (remainder m i) 0))

i]

[else (first-divisor (- i 1))]

)

)

)

(first-divisor (min m n))))

Inefficientfor large numbers!

### Analysis of Structural Recursion

• gcd-structural simply tests every number whether it divides both n and m evenly and returns the first such number

• For small natural numbers, this process works just fine

• However, for (gcd-structural 101135853 450146405) = 177 the procedure will compare 101135853 – 177 = 101135676 numbers!

• Even reasonably fast computers spend several minutes on this task.

• Enter the definition of gcd-structural into the Definitions window and evaluate the following expression… in the Interactions window … and go get a coffee 

(time (gcd-structural 101135853 450146405))

### GCD Euclidean Algorithm

• The Euclidean algorithm to determine the greatest common denominator (GCD) of two integers is one of the oldest algorithms known

• Appeared inEuclid’s Elements around 300 BC

• However, the algorithm probably was not discovered by Euclid and it may have been known up to 200 years earlier.

Insight: For two natural numbersnandm, n > m, GCD(n, m) = GCD(m, remainder(n,m))

(gcd larger smaller)

= (gcdsmaller (remainder larger smaller))

Example: GCD(18, 24) = GCD(18,remainder(24/18)) = GCD(18,6) = GCD(6,0) = 6

### GCD Generative Algorithm

;; gcd-generative : N[>= 1] N[>=1] -> N

(define (gcd-generative n m)

(local

((define (clever-gcd larger smaller)

(cond

[(= smaller 0) larger]

[else (clever-gcd

smaller

(remainder larger smaller))]))

)

(clever-gcd (max m n) (min m n))))

clever-gcdis based on generative recursion:

• The trivially solvable case is when smaller is 0

• The generative step calls clever-gcd with smaller and (remainder larger smaller)

(gcd-generative101135853450146405)needsonly9 iterations!

### GCD Euclidean Algorithm

Let n = mq + r, then any number u which dividesbothn and m (n = su and m = tu), also dividesr

r = n – qm = su – qtu = (s – qt)u

Any number v which divides both m and r(m = s’v and r = t’v ), also divides n

n = qm + r = qs´v + t´v = (s´q + t´)v

• Therefore, every common denominator of n and m is also a common denominator of m and r.

• gcd(n,m) = gcd(m,r)

• It is enough if we continue the process with m and r

• Since r is smaller in absolute value than m, we will reach r = 0 after a finite number of steps.

### Which one to use?

• Question: can we conclude that generative recursion is better than structural recursion?

• Even a well-designed generative procedure is not always faster than structural recursion.

• For example, quick-sortwins over insertion sort only for large lists

• Structural recursion is easier to design

• Designing generative recursive procedures often requires deep mathematical insight

• Structural recursion is easier to understand

• It may be difficult to grasp the idea of the generative step.

### Which one to use?

Start by using structural recursion.

If it is too slow, try to design generative recursion.

Document the problem generation with good examples, give a good termination argument.

### Outline

• Introduction to generative recursion

• Sorting with generative recursion

• Guidelines for the design of generative recursive procedures

• Structural versus generative recursion

• Backtracking: Traversing graphs

B

C

D

A

E

F

G

### Traversing Graphs

• A graph is a collection of nodes and edges.

• The edges represent one-way connections between the nodes.

• Canbe used to describe

• a plan of one-way streets in a city,

• relationships between persons,

• connections on the Internet, etc.

Scheme - list representation

(defineGraph

'((A (BE))

(B (EF))

(C (D))

(D ())

(E (C))

(F (DG))

(G ())))

D

B

C

A

E

F

G

### Traversing Graphs

;; find-route : node node graph -> (listof node)

;; to create a path from origin to destination in G

;; false, if there is no path,

(define (find-route origin destination G) ...)

(find-route 'C 'DGraph) = (list 'C 'D)

(find-route 'E 'DGraph) = (list 'E 'C 'D)

(find-route 'C 'GGraph) = false

### Backtracking Algorithms

A backtracking algorithm follows a specific template:

• Pursue a (possible) path to a solution until

• the solution is found (success! terminate), or

• the path cannot be continued.

• If the path cannot be continued:

• Walk back along the path unto the last branch where alternatives exist that have not yet been chosen,

• Choose such an alternative and proceed with step 1.

• If the starting point is reached again and there are no more alternatives: failure! terminate

Usage examples:

• "N Queens Problem", finding paths in graphs

C

D

B

A

E

F

G

### Traversing Graphs - Example

BACKTRACK !

• Find the path from node A to G!

### Traversing Graphs

• If the origin is equal to the destination, the problem is trivial; the answer is (list destination).

• Otherwise, try to find route from all neighbors of the origin.

(define (find-route originationdestinationaGraph)

(cond

[(symbol=? originationdestination)

(listdestination)]

[else ...

(find-route/list

(neighborsoriginationaGraph)

destination

aGraph)

...]))

### Neighbornodes

neighbors is similar to the function contains-doll?

;; neighbors : node graph -> (listof node)

;; to lookup the neighbors of node in graph

(define (neighbors node graph)

(cond

[(empty? graph)

(error 'neighbors "empty graph")]

[(symbol=? (first (first graph)) node)

(second (first graph))]

[else (neighbors node (rest graph))]))

### Traversing Graphs

• find-route/list

• Processes a list of nodes

• Determine, for each of them, whether a path to the destination node in this graph exists

;; find-route/list :

;; (listof node) node graph -> (listof node) or false

(define (find-route/list lo-origins destination G)

...)

• The result of find-routedepends on that of find-route/list, which can be one of these:

• a path from a neighbor node to the destination

• false, if no path from one of the neighbors could be found

### Traversing Graphs

(define (find-route originationdestinationaGraph)

(cond

[(symbol=? originationdestination)

(listdestination)]

[else

(local

((definepossible-route

(find-route/list

(neighborsoriginationaGraph)

destination

aGraph)))

(cond

[(boolean? possible-route) ...]

[else (cons? possible-route) ...]))]))

### Traversing Graphs

(define (find-route origin destination aGraph)

(cond

[(symbol=? origin destination)

(list destination)]

[else

(local ((define possible-route

(find-route/list

(neighbors origin aGraph)

destination

aGraph)))

(cond

[(boolean? possible-route) false]

[else (cons origin possible-route)]))]))

C

D

B

A

E

F

G

### Traversing Graphs - Example

• Find the path from node A to G!

Neighbors of A

### Traversing Graphs

B

C

D

B

C

D

(define (find-route/list lo-Os destaG)

(cond

[(empty? lo-Os) false]

[else

(local

((define possible-route

(find-route (first lo-Os) destaG)))

(cond

[(boolean? possible-route)

(find-route/list (rest lo-Os) destaG)]

[else possible-route])

)

])

)

A

A

E

F

G

E

F

G

### Traversing Graphs

B

C

D

B, E, C is a cycle

A

E

F

G

The function fails to terminate in a graph with a cycle:

(find-route 'B 'D Cyclic-graph)

= ... (find-route/list (list'E 'F) 'D Cyclic-graph) ...

= ... (find-route 'E 'D Cyclic-graph) ...

= ... (find-route/list (list'C 'F) 'D Cyclic-graph) ...

= ... (find-route 'C 'D Cyclic-graph) ...

= ... (find-route/list (list'B 'D) 'D Cyclic-graph) ...

= ... (find-route 'B 'D Cyclic-graph) ...

= ...

### Summary

• There are problems that cannot be solved, or can only be solved sub-optimally, using structural recursion

• Generative recursion is based on the principle: “divide-and-conquer”

• The design recipe for generative recursive functions has to be adapted:

• In particular, we need an argument for the termination

• Structurally recursive functions are a subset of generative recursive functions

• When both strategies are possible the choice depends on a case-by-case basis

• one cannot say that one class is better than the other