Any pointers on how to solve efficiently the following function in Haskell, for large numbers (n > 108)
f(n) = max(n, f(n/2) + f(n/3) + f(n/4))
I’ve seen examples of memoization in Haskell to solve fibonacci
numbers, which involved computing (lazily) all the fibonacci numbers
up to the required n. But in this case, for a given n, we only need to
compute very few intermediate results.
Thanks
We can do this very efficiently by making a structure that we can index in sub-linear time.
But first,
Let’s define
f, but make it use ‘open recursion’ rather than call itself directly.You can get an unmemoized
fby usingfix fThis will let you test that
fdoes what you mean for small values offby calling, for example:fix f 123 = 144We could memoize this by defining:
That performs passably well, and replaces what was going to take O(n^3) time with something that memoizes the intermediate results.
But it still takes linear time just to index to find the memoized answer for
mf. This means that results like:are tolerable, but the result doesn’t scale much better than that. We can do better!
First, let’s define an infinite tree:
And then we’ll define a way to index into it, so we can find a node with index
nin O(log n) time instead:… and we may find a tree full of natural numbers to be convenient so we don’t have to fiddle around with those indices:
Since we can index, you can just convert a tree into a list:
You can check the work so far by verifying that
toList natsgives you[0..]Now,
works just like with list above, but instead of taking linear time to find each node, can chase it down in logarithmic time.
The result is considerably faster:
In fact it is so much faster that you can go through and replace
IntwithIntegerabove and get ridiculously large answers almost instantaneouslyFor an out-of-the-box library that implements the tree based memoization, use MemoTrie: