Reputation: 59
is there a way to optimize this? It is taking way too long to run
let counter2 = ref 0
let rec s2 num =
counter2 := !counter2 + 1;
match num with
| 0 -> 1
| 1 -> 2
| _ -> (((((6*num)-3) * (s2 (num-1))) / (num+1))) - (((num-2)* (s2 (num-2))/(num+1)))
Upvotes: 1
Views: 116
Reputation: 36601
Let's clean this up a bit. This is just your approach, but tidied up by taking advantage of order of operations and binding some names to subexpressions. I've also used incr counter2
rather than counter2 := !counter2 + 1
.
let counter2 = ref 0
let rec s2 num =
incr counter2;
match num with
| 0 -> 1
| 1 -> 2
| _ ->
let s2' = s2 (num - 1) in
let s2'' = s2 (num - 2) in
let num' = num + 1 in
(6 * num - 3) * s2' / num' - (num-2) * s2'' / num'
Now the efficiency issue with this code is that s2
gets called more than it needs to. If we evaluate s2 4
for instance:
s2 4
s2 3
s2 2
s2 1
s2 0
s2 1
s2 2
s2 1
s2 0
For s2 5
:
s2 5
s2 4
s2 3
s2 2
s2 1
s2 0
s2 1
s2 2
s2 1
s2 0
s2 3
s2 2
s2 1
s2 0
s2 1
So there are 9 and 15 calls respectively to s2
in those two calls. But how many unique
calls are there? 5 and 6, respectively.
If we cache the results of these calculations in something like a map, we can significantly reduce the complexity of the function, since map lookups are in a worst case situation O(log n).
We then just have to pass a map as an argument to the function and have the function either lookup the cached result, or calculate it and cache it, passing that cache along to the next call to avoid repeated calls.
"Hiding" this function inside an s3
function allows us to make the map "invisible" to anyone calling s3
and initialize it with the bindings for 0 and 1.
module M = Map.Make (Int)
let s3 num =
let counter = ref 0 in
let rec aux num map =
incr counter;
match M.find_opt num map with
| Some v -> (v, map)
| None ->
let (s3', map') = aux (num - 1) map in
let (s3'', map'') = aux (num - 2) map' in
let num' = num + 1 in
let result = (6 * num - 3) * s3' / num' - (num-2) * s3'' / num' in
(result, M.add num result map'')
in
(!counter, fst @@ aux num @@ M.(empty |> add 0 1 |> add 1 2))
If we call s2 10_000
, expect to be waiting a very long time. Calling s3 10_000
is almost instant. Of course, this will overflow the int
type, but it'll do so quickly.
# s3 10_000;;
- : int * int = (19999, -159516254014960)
Upvotes: 0
Reputation: 66803
Here is the highly recursive definition of the Fibonacci sequence:
let rec fib n =
if n < 2 then n
else fib (n - 2) + fib (n - 1)
Here is the not so recursive definition of the Fibonacci sequence.
let nfib n =
let rec helper pprev prev i =
if i = n then
pprev + prev
else
helper prev (pprev + prev) (i + 1)
in
if n < 2 then n
else helper 0 1 2
Here is a function for timing things:
let time f x =
let st = Unix.gettimeofday () in
let res = f x in
Printf.printf "%f seconds\n" (Unix.gettimeofday () -. st);
res
Here are times for the fib
and nfib
functions:
# time fib 42;;
7.694294 seconds
- : int = 267914296
# time nfib 42;;
0.000002 seconds
- : int = 267914296
Upvotes: 3