1.10 Recursion A recursive definition of a term is a definition that involves the term itself in the definition of the term. A recursive formula (or recursive function) is a formula (or function) that involves itself in its definition. A recursive function in a computer program is one that calls itself. In the last two cases you need to be careful you don't get caught in an infinite loop. Example 1. Factorials. Recall that the factorial of a positive integer n is the product of all the integers from 1 to n, i.e. n! = 123…n For example 4! = 1234 = 24 Many books make the special definition in the case that n = 0 by setting 0! = 1. In order to make this into a recursive definition, note that n! = 123…(n-1)n = [123…(n-1)] n = n [(n-1)!] Thus n! = n [(n-1)!] Notice that this formula for n! involves a factorial on the right. It is this aspect that makes the formula recursive. However, this formula is not a complete definition of n!. We need to augment it by the special case where n = 0. So a complete recursive definition of n! would be the following. (1) n [(n-1)!] n! = 1 if n 1 if n = 0 To use this definition to compute 4! we would proceed as follows. Putting n = 4 into the definition (1) we get (2) 4! = 4(4-1)! = 43! At this point we leave off the calculation of 4! and use the definition to compute 3!. Putting n = 3 into (1) gives 1.10 - 1 (3) 3! = 3(3-1)! = 32! At this point we leave off the calculation of 3! and use (1) to compute 2!. (4) 2! = 2(2-1)! = 21! Now we use (1) with n = 1. (5) 1! = 1(1-1)! = 10! Now we use (1) with n = 0. However, in this case (1) simply says 0! = 1 We put this into (5) giving 1! = 10! = 11 = 1 We put this into (4) giving 2! = 21! = 21 = 2 We put this into (3) giving 3! = 32! = 32 = 6 Finally, we put this into (2) giving 4! = 43! = 46 = 24 The definition (1) can be used as the basis of a recursive function in a computer program. For example, in C one could convert (1) into the following. int factorial( int n ) { if (n == 0) return 1; else return n * factorial( n – 1 ); } The recursive computation of 4! above amounted to a loop. It turns out that in computer programming, many things that can be done with a loop can be done with a recursive function instead. Example 2. Consider the computation of the sum of the squares of the consecutive integers from 1 to n, i.e. 1.10 - 2 12 + 22 + 32 + … + n2 For example, if n = 4 we would get 12 + 22 + 32 + 42 = 1 + 4 + 9 + 16 = 30 In order to make this into a recursive definition, we give the sum a name and note that (6) Sn = 12 + 22 + … + (n-1)2 + n2 = Sn-1 + n2 For example, S4 = 12 + 22 + 32 + 42 = S3 + 42 We need to augment (6) for the special case where n = 0 by setting S0 = 0. So a complete recursive definition of Sn would be the following. (7) Sn Sn-1 + n2 = 0 if n 1 if n = 0 For example, in C one could convert (7) into the following. int S( int n ) { if (n == 0) return 0; else return S(n – 1) + n2; } Recursive definitions of functions defined for positive integers are often called recurrence relations or difference equations. We shall look at these in more detail in chapter 5. Recursive definitions can also be used for character strings. Example 3. The reversal of a string is the new string with the characters in the original string arranged in the reverse order. Thus if s = "c1c2c3…cn" is the string consisting of the characters c1, c2, c3, …, cn then rev(s) = reversal of s = "cncn-1…c2c1" For example if s = "john", then rev(s) = "nhoj". In order to make this into a recursive formula, note that 1.10 - 3 rev("c1c2c3…cn") = "cn" + "cn-1…c2c1" = "cn" + rev("cn-1…c2c1") = sn + rev(s1,n-1) = sLen(s) + rev(s1,Len(s)-1) where + = concatenation of strings sj = the string consisting of the jth character of s sj,k = the string consisting of characters j through k of s Len(s) = the number of characters in s. We supplement this formula by defining the reversal of the null string "" to be the null string itself. So (8) if s "" sLen(s) + rev(s1,Len(s)-1) rev(s) = "" if s = "" To use this definition to compute rev("john") we would proceed as follows. rev("john") = "n" + rev("joh") rev("joh") = "h" + rev("jo") rev("jo") = "o" + rev("j") rev("j") = "j" + rev("") rev("") = "" rev("j") = "j" + "" = "" rev("jo") = "o" + "j" = "oj" rev("joh") = "h" + "oj" = "hoj" rev("john") = "n" + "hoj" = "nhoj" For example, in C one could convert (8) into the following. 1.10 - 4 string rev(string s) { int n; n = s.length(); if (s == "") return s; else return s[n-1] + rev(s.substr(0,n-1)); } Sometimes a recursive definition is simpler than a non-recursive one. Example 4 (The Fibonacci sequence). The Fibonacci sequence f0, f1, f2, …, fn, … starts out with f0 = 1 and f1 = 1. After that each element is the sum of the two previous elements. See table at right. A recursive definition is (9) fn fn-2 + fn-1 = 1 if n 2 if n = 0 or n = 1 In C one could convert (9) into the following. int f( int n ) { if ((n == 0) || (n == 1)) return 1; else return f(n – 2) + f(n – 1); } n 0 1 2 3 4 5 6 7 8 9 10 fn 1 1 2 3 5 8 13 21 34 55 89 We shall show later that fn n n 5 + 1 1 + 5 5 - 1 1 - 5 + = 2 5 2 2 5 2 0.72 (1.62)n + 0.28 (- 0.62)n Since (- 0.62)n 0 as n it follow that for large n one has fn 0.72 (1.62)n i.e. the fn grow exponentially fast with n. Even though the recursive definition of fn allows one to write a simple computer program to compute fn, in this case the recursive program runs much slower than a non-recursive one since the recursive program doesn't take advantage of the previous computation of f(n – 2) when it computes f(n – 1). Here is a non-recursive version. 1.10 - 5 int f( int n ) { int fn, fnm1, j; fnm1 = 1; fn = 1; for (j = 2; j <= n, j++) { nextf = fn + fnm1; fnm1 = fn; fn = nextf; } else return fn; } 1.10 - 6