My exercise, that you can see here, says that I need to implement a recursive version of C(n, k).
That's my solution:
module LE1.Recursao.Combinacao where
combina :: Integral a => a -> a -> a
combina n k | k == 0 = 1
| n == k = 1
combina n k | k > 0 && n > k = (combina (n - 1) k) + (combina (n - 1) (k - 1))
combina _ _ = 0
Now I want to create a tail-recursive version of this function, so I don't get stack overflows for large numbers and also calculate the combination quicker!
I'm new to tail call optimisation, but I did that in Elixir for the Fibonacci series:
defmodule Fibonacci do
def run(n) when n < 0, do: :error
def run(n), do: run n, 1, 0
def run(0, _, result), do: result
def run(n, next, result), do: run n - 1, next + result, next
end
I understand this Fibonacci code and I think that the combination algorithm isn't too different, but I don't know how to start.
Daniel Wagner writes
This can get rather inefficient for large
nand medium-sizek; the multiplications get huge. How might we keep them smaller? We can write a simple recurrence:Putting that all together,
Just one problem remains: this definition is not tail recursive. We can fix that by counting up instead of down:
Don't worry about the
n `seq`; it's of very little consequence.Note that this implementation uses
O(min(k,n-k))arithmetic operations. So ifkandn-kare both very large, it will take a long time. I don't know if there's any efficient way to get exact results in that situation; I believe that binomial coefficients of that sort are usually estimated rather than calculated precisely.