• Tidak ada hasil yang ditemukan

Iterative versus Recursive Algorithms

Recursive algorithms can often be rewritten to use iterative loops instead, and vice versa; it is a matter of elegance and clarity that dictates which tech- nique is easier to use. Consider the problem of sorting a list of integers into ascending order.

2.6 Iterative versus Recursive Algorithms 29

Sorting Problem:

Sort a list of integers.

Input:A list ofndistinct integersa= (a1, a2, . . . , an).

Output: Sorted list of integers, that is, a reorderingb = (b1, b2, . . . , bn)of integers fromasuch thatb1 < b2 <· · · <

bn.

The following algorithm, called SELECTIONSORT, is a naive but simple it- erative method to solve the Sorting problem. First, SELECTIONSORTfinds the smallest element ina, and moves it to the first position by swapping it with whatever happens to be in the first position (i.e.,a1). Next, SELECTIONSORT

finds the second smallest element ina, and moves it to the second position, again by swapping witha2. At theith iteration, SELECTIONSORTfinds theith smallest element ina, and moves it to theith position. This is an intuitive ap- proach at sorting, but is not the best-known one. Ifa= (7,92,87,1,4,3,2,6), SELECTIONSORT(a,8)takes the following seven steps:

(7,92,87,1,4,3,2,6) (1,92,87,7,4,3,2,6) (1,2,87,7,4,3,92,6) (1,2,3,7,4,87,92,6) (1,2,3,4,7,87,92,6) (1,2,3,4,6,87,92,7) (1,2,3,4,6,7,92,87) (1,2,3,4,6,7,87,92)

SELECTIONSORT(a, n) 1 for i←1ton−1

2 aj ← Smallest element amongai, ai+1, . . . an. 3 Swapaiandaj

4 returna

Line2of SELECTIONSORTfinds the smallest element over all elements ofa that come afteri, and fits nicely into a subroutine as follows. The subroutine

INDEXOFMIN(array, first, last)works witharrayand returns the index of the smallest element between positions first and lastby examining each element fromarrayfirsttoarraylast.

INDEXOFMIN(array, first, last) 1 index←first

2 for k←first+ 1tolast 3 if arrayk < arrayindex

4 index←k

5 returnindex

For example, ifa= (7,92,87,1,4,3,2,6), then INDEXOFMIN(a,1,8)would be4, sincea4 = 1is smaller than any other element in(a1, a2, . . . , a8). Sim- ilarly, INDEXOFMIN(a,5,8) would be 7, since a7 = 2 is smaller than any other element in(a5, a6, a7, a8). We can now write SELECTIONSORT using this subroutine.

SELECTIONSORT(a, n) 1 for i←1ton−1

2 j←INDEXOFMIN(a, i, n) 3 Swap elementsaiandaj 4 returna

To illustrate the similarity between recursion and iteration, we could in- stead have written SELECTIONSORTrecursively (reusing INDEXOFMINfrom above):

RECURSIVESELECTIONSORT(a, first, last) 1 if first < last

2 index←INDEXOFMIN(a, first, last) 3 Swapafirstwithaindex

4 a←RECURSIVESELECTIONSORT(a, first+ 1, last) 5 returna

In this case, RECURSIVESELECTIONSORT(a,1, n)performs exactly the same operations as SELECTIONSORT(a, n).

It may seem contradictory at first that RECURSIVESELECTIONSORTcalls it- self to get an answer, but the key to understanding this algorithm is to realize that each time it is called, it works on a smaller set of elements from the list until it reaches the end of the list; at the end, it no longer needs to recurse.

2.6 Iterative versus Recursive Algorithms 31 The reason that the recursion does not continue indefinitely is because the al- gorithm works toward a point at which it “bottoms out” and no longer needs to recurse—in this case, whenfirst=last.

As convoluted as it may seem at first, recursion is often the most natural way to solve many computational problems as it was in the Towers of Hanoi problem, and we will see many recursive algorithms in the coming chapters.

However, recursion can often lead to very inefficient algorithms, as this next example shows.

The Fibonacci sequence is a mathematically important, yet very simple, progression of numbers. The series was first studied in the thirteenth century by the early Italian mathematician Leonardo Pisano Fibonacci, who tried to compute the number of offspring of a pair of rabbits over the course of a year (fig. 2.4). Fibonacci reasoned that one pair of adult rabbits could create a new pair of rabbits in about the same time that it takes bunnies to grow into adults. Thus, in any given period, each pair of adult rabbits produces a new pair of baby rabbits, and all baby rabbits grow into adult rabbits.14 If we letFnrepresent the number of rabbits in periodn, then we can determine the value ofFn in terms of Fn−1 and Fn−2. The number of adult rabbits at time periodnis equal to the number of rabbits (adult and baby) in the previous time period, orFn−1. The number of baby rabbits at time period nis equal to the number of adult rabbits inFn−1, which isFn−2. Thus, the total number of rabbits at time periodn is the number of adults plus the number of babies, that is,Fn=Fn−1+Fn−2, withF1=F2= 1. Consider the following problem:

Fibonacci Problem:

Calculate thenth Fibonacci number.

Input:An integern.

Output:Thenth Fibonacci numberFn=Fn−1+Fn−2(with F1=F2= 1).

The simplest recursive algorithm, shown below, calculates Fn by calling itself to computeFn−1andFn−2. As figure 2.5 shows, this approach results in a large amount of duplicated effort: in calculatingFn−1we find the value

14. Fibonacci faced the challenge of adequately formulating the problem he was studying, one of the more difficult parts of bioinformatics research. The Fibonacci view of rabbit life is overly simplistic and inadequate: in particular, rabbits never die in his model. As a result, after just a few generations, the number of rabbits will be larger than the number of atoms in the universe.

1pair 1pair 2pairs 3pairs 5pairs 8pairs Figure 2.4 Fibonacci’s model of rabbit expansion. A dashed line from a pair of big rabbits to a pair of little rabbits means that the pair of adult rabbits had bunnies.

ofFn−2, but we calculate it again from scratch in order to determineFn. Therefore, most of the effort in this algorithm is wasted recomputing values that are already known.