Tail-Recursive Lucas Sequence Computation in OCaml Example

In OCaml, implement a function lucas : int -> int such that lucas n is the -th Lucas number. On 64-bit platforms, only can be represented as values of type int.


Solution

Disregarding the input domain check, a naive implementation of the lucas function would be as follows:

ocaml
let rec lucas : int -> int =
  function
  | 0 -> 2
  | 1 -> 1
  | n -> lucas (n - 1) + lucas (n - 2)

In terms of runtime complexity, this implementation is inefficient since there are duplicate calls to lucas occurring in the computation of lucas (n - 1) + lucas (n - 2). Space complexity isn't better either since those calls to lucas in lucas (n - 1) + lucas (n - 2) are not tail calls, which means that new stack frames are allocated whenever the recursion depth increases.

To address both issues, we can generalize the problem of finding to the problem of finding when we're given and . We define a tail-recursive helper function lucas_helper : int -> int -> int -> int such that lucas_helper n a b is when a is , and b is . These extra parameters a and b act as a memoization mechanism for the latest two Lucas numbers computed.

ocaml
let lucas : int -> int =
  let rec lucas_helper : int -> int -> int -> int =
    fun n a b ->
      if n = 0 then
        a
      else
        lucas_helper (n - 1) b (a + b)
  in
  fun n ->
    if n < 0 || n > 88 then
      raise (Invalid_argument "lucas")
    else
      lucas_helper n 2 1

Alternatively, without using recursion, we would iterate from 0 to n - 1 and successively compute using mutable references.

ocaml
let lucas : int -> int =
  fun n ->
    if n < 0 || n > 88 then
      raise (Invalid_argument "lucas")
    else
      let i : int ref = ref 0
      and a : int ref = ref 2
      and b : int ref = ref 1
      in
      while !i < n do
        let next : int = !a + !b in
        a := !b;
        b := next;
        i := !i + 1
      done;
      !a