6.19 Example Using Recursion: Fibonacci Series

The Fibonacci series


0, 1, 1, 2, 3, 5, 8, 13, 21, …

begins with 0 and 1 and has the property that each subsequent Fibonacci number is the sum of the previous two Fibonacci numbers.

The series occurs in nature and, in particular, describes a form of spiral. The ratio of successive Fibonacci numbers converges on a constant value of 1.618…. This number frequently occurs in nature and has been called the golden ratio or the golden mean. Humans tend to find the golden mean aesthetically pleasing. Architects often design windows, rooms and buildings whose length and width are in the ratio of the golden mean. Postcards are often designed with a golden mean length/width ratio. A web search for “Fibonacci in nature” reveals many interesting examples, including flower petals, shells, spiral galaxies, hurricanes and more.

Recursive Fibonacci Definition

The Fibonacci series can be defined recursively as follows:


fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(n) = fibonacci(n – 1) + fibonacci(n – 2)

The program of Fig. 6.26 calculates the nth Fibonacci number recursively by using function fibonacci. Fibonacci numbers tend to become large quickly, although slower than factorials do. Therefore, we chose the data type unsigned long for the parameter type and the return type in function fibonacci. Figure 6.26 shows the execution of the program, which displays the Fibonacci values for several numbers.

Fig. 6.26 Recursive function fibonacci.

Alternate View

 1   // Fig. 6.26: fig06_26.cpp
 2   // Recursive function fibonacci.
 3   #include <iostream>
 4   using namespace std;
 5
 6   unsigned long fibonacci(unsigned long); // function prototype
 7
 8   int main() {
 9      // calculate the fibonacci values of 0 through 10
10      for (unsigned int counter{0}; counter <= 10; ++counter)
11         cout << "fibonacci(" << counter << ") = "
12            << fibonacci(counter) << endl;
13
14      // display higher fibonacci values
15      cout << "
fibonacci(20) = " << fibonacci(20) << endl;
16      cout << "fibonacci(30) = " << fibonacci(30) << endl;
17      cout << "fibonacci(35) = " << fibonacci(35) << endl;
18   }
19
20   // recursive function fibonacci
21   unsigned long fibonacci(unsigned long number) {
22      if ((0 == number) || (1 == number)) { // base cases
23         return number;
24      }
25      else { // recursion step
26         return fibonacci(number - 1) + fibonacci(number - 2);
27      }
28   }

fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(2) = 1
fibonacci(3) = 2
fibonacci(4) = 3
fibonacci(5) = 5
fibonacci(6) = 8
fibonacci(7) = 13
fibonacci(8) = 21
fibonacci(9) = 34
fibonacci(10) = 55

fibonacci(20) = 6765
fibonacci(30) = 832040
fibonacci(35) = 9227465

The application begins with a for statement that calculates and displays the Fibonacci values for the integers 0–10 and is followed by three calls to calculate the Fibonacci values of the integers 20, 30 and 35 (lines 15–17). The calls to fibonacci in main (lines 12 and 15–17) are not recursive calls, but the calls from line 26 of fibonacci are recursive. Each time the program invokes fibonacci (lines 21–28), the function immediately tests the base case to determine whether number is equal to 0 or 1 (line 22). If this is true, line 23 returns number. Interestingly, if number is greater than 1, the recursion step (line 26) generates two recursive calls, each for a slightly smaller problem than the original call to fibonacci.

Evaluating fibonacci(3)

Figure 6.27 shows how function fibonacci would evaluate fibonacci(3). This figure raises some interesting issues about the order in which C++ compilers evaluate the operands of operators. This is a separate issue from the order in which operators are applied to their operands, namely, the order dictated by the rules of operator precedence and associativity. Figure 6.27 shows that evaluating fibonacci(3) causes two recursive calls, namely, fibonacci(2) and fibonacci(1). In what order are these calls made?

Fig. 6.27 Set of recursive calls to function fibonacci.

Order of Evaluation of Operands

Most programmers simply assume that the operands are evaluated left to right. C++ does not specify the order in which the operands of most operators (including +) are to be evaluated. Therefore, you must make no assumption about the order in which these calls execute. The calls could in fact execute fibonacci(2) first, then fibonacci(1), or they could execute in the reverse order: fibonacci(1), then fibonacci(2). In this program and in most others, it turns out that the final result would be the same. However, in some programs the evaluation of an operand can have side effects (changes to data values) that could affect the final result of the expression.

C++ specifies the order of evaluation of the operands of only four operators—&&, ||, comma (,) and ?:. The first three are binary operators whose two operands are guaranteed to be evaluated left to right. The last operator is C++’s only ternary operator—its leftmost operand is always evaluated first; if it evaluates to true, the middle operand evaluates next and the last operand is ignored; if the leftmost operand evaluates to false, the third operand evaluates next and the middle operand is ignored.

Portability Tip 6.3

Programs that depend on the order of evaluation of the operands of operators other than &&, ||, ?: and the comma (,) operator can function differently with different compilers and can lead to logic errors.

 

Common Programming Error 6.12

Writing programs that depend on the order of evaluation of the operands of operators other than &&, ||, ?: and the comma (,) operator can lead to logic errors.

 

Error-Prevention Tip 6.12

Do not depend on the order in which operands are evaluated. To ensure that side effects are applied in the correct order, break complex expressions into separate statements.

 

Common Programming Error 6.13

Recall that the && and || operators use short-circuit evaluation. Placing an expression with a side effect on the right side of a && or || operator is a logic error if that expression should always be evaluated.

Exponential Complexity

A word of caution is in order about recursive programs like the one we use here to generate Fibonacci numbers. Each level of recursion in function fibonacci has a doubling effect on the number of function calls; i.e., the number of recursive calls that are required to calculate the nth Fibonacci number is on the order of 2n. This rapidly gets out of hand. Calculating only the 20th Fibonacci number would require on the order of 220 or about a million calls, calculating the 30th Fibonacci number would require on the order of 230 or about a billion calls, and so on. Computer scientists refer to this as exponential complexity. Problems of this nature can humble even the world’s most powerful computers as n becomes large! Complexity issues in general, and exponential complexity in particular, are discussed in detail in the upper-level computer science course typically called Algorithms.

Performance Tip 6.5

Avoid Fibonacci-style recursive programs that result in an exponential “explosion” of calls.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset