Version 4.1.31
Handouts: TailRecursion.hs, quicksort.hs, mergesort.hs, fibonacci.hs
Developing the recursive solution, starting small:
Recursion:
Second example: printing a linked list backwards.
Demonstrating that a recursive method is correct (p. 230):
power in TailRecursion.hsRecurrence relations:
T(N) = Ta if base case;
T(N) = Tb otherwise.
Typographical error on p. 234: Θ(2n) should be Θ(2n)
A[N-1] as pivot p.The recurrence relation: assuming that partitioning an array of size N divides it into two nearly equal-sized parts (approximately N/2 each):
T(N) = 1, if N = 1;
T(N) = N + T(N/2) + T(N/2 − 1), otherwise.
Modifying the algorithm to pivot on a randomly selected element, instead of the last element.
Quick-sort with Hungarian folk dance (YouTube, 6:55)
The recurrence relation:
T(N) = 1, if N = 1;
T(N) = N + 2 T(N/2), otherwise.
Its solution: T(N) = N (1 + log2 N), which is in Θ(N log N).
powerTR, powerTRloop in TailRecursion.hsWhat the author does not say:
Don't optimize code prematurely and unnecessarily. While it is true that in many programming languages, recursive functions or methods are less efficient than iterative ones, and functional operations may be even less efficient, we always should remember that clarity is usually more important than efficiency except in time-critical sections of code.
However, we can do better than this. The array uses Θ(N) space, but we only use the two most recently set elements. These can be represented by just two int variables. This can be done iteratively:
def fibn (n):
a = 1 // fib(0), will store fibn(j - 2) in the loop
b = 1 // fib(1), will store fibn(j - 1)
loop for j = 2 to n:
c = a + b // = fibn(j)
a = b
b = c
end loop // so now j == n
return c
or recursively:
def fibr (n):
if n < 2:
return 1
else
// fib(0) = 1, fib(1) = 1, fib(2) is next
return fibrr (1, 1, 2, n)
def fibrr (a, b, i, n):
c = a + b // = fib(i)
if i == n:
return c
else
return fibrr (b, c, i + 1, n)Haskell (both versions): fibonacci.hs
power and powerTR Haskell examples.