• Tidak ada hasil yang ditemukan

There is an important class of calculations that can be implemented with recursive algorithms. A standard example is the factorial function n!, defined for non-negative integers as:

n! = 1 for n = 1 or n = 0 n! = n(n−1)! for n > 1

For example, 5!=54321=120. This is a recursive definition, in which each value of n! for n greater than 1 is defined in terms of (n−1)!.

Like other modern programming languages, PHP supports recursive functions—functions that call themselves. Document 7.3 implements the recursive algorithm for n!.

Document 7.3 (factorial.php)

<html><head><title>calculate n!</title>

</head><body>

<h3>Calculate n!</h3>

<formaction="<?php $_SERVER['PHP_SELF'];?>"method="post">

Enter n (integer >=0):

<inputtype="text"name="n"value="8"size="3" /><br />

<inputtype="submit"name="submit"value="Calculate n!" />

</form>

<?php

$n=$_POST["n"];

echo"max integer: ".PHP_INT_MAX."<br />"; echo"Calculate n! for n=".$n."<br />";

$nfact=nFactorial($n);

echo $nfact."<br />"; function nFactorial($n) {

if($n<=1)return1;

else return $n*nFactorial($n-1);

}

?>

</body></html>

The shaded line contains the critical code, in which the function calls itself. For certain mathematical functions, such as n!, the structure

of the recursive function is easy to see from the mathematical definition of the function. Recursive algorithms always require at least two branches. One branch generates a recursive call and the other terminates the function. In Document 7.3, the relationship between the recursive definition for n! and the code required to evaluate n! should be obvious. Note that this code does not check to make sure that only non-negative integer values of n have been entered as input. (You might want to add that code yourself.) Also, there can be problems with large values of n because of the limited range of integers; n!

grows rapidly with increasing n. The maximum allowed integer is system- dependent. Document 7.3 displays the value ofPHP_INT_MAXwhich, as its name implies, tells you what the maximum integer value is for your system.

However, for larger values, the result will be converted to afloating point number. For the PHP installation used to execute Document 7.3, the code gives 18! = 6.402373705728E+15 (the right value), but 19! = 1.2164510040883E +17, for which the correct (integer) value is 121645100408832000.

The success of recursive functions depends on the function model discussed at the beginning of this chapter, in which informationflows into a function through the parameter list. When the function is called with the current value of n–1, this value is associated with the parameter n in the new call. Because of how the algorithm is written, the local value of n–1 will eventually equal 1 (for any value of n originally greater than 1) and the recursive calls will be terminated. The intermediate values of the factorial function are stored within the programming environment. Table 7.1shows the sequence of events for calculating 4!.

You can think of each function call as adding a plate to a stack of plates.

The initial call plus the three recursive calls add a total of four plates to the stack. At the third recursive call, n = 1 and a value of 1 is returned. Executing a returnstatement is equivalent to removing one of the plates. Subsequently, the three remaining plates are removed as the deferred multiplications are

136 7 Functions

carried out and a value is returned. When the function returns control of the script back to the point from which it was initially called, all the“plates”have been removed from the stack.

For more complicated recursive algorithms, it can be difficult to follow the course of the calculations. Fortunately, it isn’t necessary to do this. As long as the algorithm is properly designed, with a condition that will appropriately terminate the recursive calls, the programming environment takes care of keeping track of all the intermediate values generated during the execution of the algorithm.

Here’s another example of a well-known function that is defined recursively. The Fibonacci numbers Fnthat form the sequence 1, 1, 2, 3, 5, 8, 13, 21,… are defined for positive integer values of n as

Fn¼1 if n¼1 or n¼2 Fn¼ Fn1þ Fn2if n[2

Document 7.4 shows how simple it is to evaluate this function using a recursive algorithm.

Document 7.4 (bonacci.php)

<?php

$n=8;

echo"Calculate the rst ".$n." Fibonacci numbers.<br />";

for($i=1;$i<=$n;$i++) {

echo $i." ".Fib($i)."<br />";

}

function Fib($n) { if($n<=2) return1;

else return Fib($n-1)+Fib($n-2);

}

?>

Table 7.1 Calculating 4! using a recursive algorithm

Local value of n Action Value returned

n = 4 Initial call Deferred

n = 3 1strecursive call Deferred

n = 2 2ndrecursive call Deferred

n = 1 3rdrecursive call 1

n = 2 Complete multiplication 21 2

n = 3 Complete multiplication 32 6

n = 4 Complete multiplication 46 24

It is not easy to follow the sequence of events as this recursive algorithm does its job. However, you don’t have to worry about that!

Recursive algorithms can also be formulated using count-controlled or conditional loop structures. However, a recursive formulation is often much shorter and more direct to implement

in code. The famous“Towers of Hanoi”problem is an excellent example of a problem that is difficult to solve“directly”but is trivial to solve recursively.

Consider three poles, on one of which are stacked 64 golden rings. The bottom ring is the largest and the others decrease in size. The object is to move the 64 rings from one pole to another, using the remaining pole as a temporary storage place for rings. There are two rules for moving rings:

1. Only one ring can be moved at a time.

2. A ring can never be placed on top of a smaller ring.

Describe how to move the entire stack of rings from one pole to another.

It can be shown that it will take 2n–1 moves to move n rings. For n = 64, if you could move one ring per second without ever making a mistake, it would take roughly 100 times the estimated age of the universe!

However, it is not difficult to develop a recursive algorithm that will work in prin- ciple for any number of rings and apply it to a value of n that is small enough to be practical. For n = 4, 15 moves are required.

In a conceptual sense, the solution is simple (but perhaps not obvious). Suppose the poles are labeled A, B, and C. Initially, all the rings, numbered 1 through n, smallest

to largest, are on A and the goal is to move them all to C. The steps are:

138 7 Functions

1. Move n– 1 rings from A to B.

2. Move the nth ring from A to C.

3. Move n– 1 rings from B to C.

This solution is“conceptual”in the sense that it doesn’t specify precisely how to do steps 1 and 3; only step 2 defines a specific action that can be taken. However, the power of recursive functions allows us to solve this problem without giving additional specific steps, as shown in Document 7.5.

Document 7.5 (towers.htm)

<?php

$n=4;

echo"Towers of Hanoi moves for ".$n." rings.<br />";

move($n,'A','C',B);

function move($n,$start,$end,$intermediate) { if($n> 0) {

move($n-1,$start,$intermediate,$end);

echo"Move ring ".$n." from ".$start." to ".$end."<br />";

move($n-1,$intermediate,$end,$start);

} }

?>

Although this seems almost too simple to be true, in fact this simple

“conceptual”code is all that is required to solve this problem in the sense that all the steps are explicitly written out; try it yourself using a real set of rings or blocks. You can also try writing an equivalent non-recursive solution.

(Good luck!)

The success of this algorithm depends, once again, on how parameter lists work—passing information into a function along a “one-way street.”

This algorithm will generatemanyintermediate values and, as n increases it will eventually run out of memory space; it still works for n = 10, which requires 1023 moves.