Recursion
March 12, 2019
The factorial function is easily written in recursive form:
(define (factorial n) (if (zero? n) 1 (* n (factorial (- n 1)))))
That’s not tail recursive, because the result of the recursive call to fact
is then multiplied by n. So this is a better version of the function, where the inner fact
function is tail recursive:
(define (factorial n) (let fact ((n n) (result 1)) (if (zero? n) result (fact (- n 1) (* n result)))))
The result is the same in either case:
> (factorial 7) 5040
The recursive version of the fibonacci function is based on the mathematical definition of the function:
(define (fibonacci n) (if (< n 2) 1 (+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
That function has an O(2n) time complexity; even a calculation as small as (fibonacci 50)
is extremely slow:
> (time (fibonacci 50)) (time (fibonacci 50)) no collections 360.035843217s elapsed cpu time 361.197806893s elapsed real time 0 bytes allocated 20365011074
That took six minutes! We can make it faster by building up the entire fibonacci sequence to n in time proportional to O(n):
(define (fibonacci n) (let fib ((k 1) (fk 1) (fk-1 1)) (if (= k n) fk (fib (+ k 1) (+ fk fk-1) fk))))
> (time (fibonacci 50)) (time (fibonacci 50)) no collections 0.000000567s elapsed cpu time 0.000000000s elapsed real time 0 bytes allocated 20365011074
Wow! It’s possible to do even better, computing the nth fibonacci number in time O(log n), but that’s more to do with mathematicians than with student programmers; you can see how we did it in a previous exercise.
You can run the program at https://ideone.com/W3ba7C.
In Python. I add a lru_cache for fibonacci, as otherwise it may never finish. I try to avoid recursion in Python, as there is also a recursion limit in Python.
A Haskell version.
Here’s a solution in Common Lisp.
Example Usage: