Podemos fazer isso de maneira muito eficiente, criando uma estrutura que podemos indexar em tempo sublinear.
Mas primeiro,
{-# LANGUAGE BangPatterns #-}
import Data.Function (fix)
Vamos definir f
, mas faça com que use 'recursão aberta' em vez de se chamar diretamente.
f :: (Int -> Int) -> Int -> Int
f mf 0 = 0
f mf n = max n $ mf (n `div` 2) +
mf (n `div` 3) +
mf (n `div` 4)
Você pode obter um não-personalizado f
usandofix f
Isso permitirá que você teste f
o que você quer dizer com pequenos valores f
chamando, por exemplo:fix f 123 = 144
Podemos memorizar isso definindo:
f_list :: [Int]
f_list = map (f faster_f) [0..]
faster_f :: Int -> Int
faster_f n = f_list !! n
Isso tem um desempenho razoavelmente bom e substitui o que levaria tempo O (n ^ 3) por algo que memorizasse os resultados intermediários.
Mas ainda leva tempo linear apenas para indexar e encontrar a resposta memorizada mf
. Isso significa que resultados como:
*Main Data.List> faster_f 123801
248604
são toleráveis, mas o resultado não é muito melhor que isso. Nós podemos fazer melhor!
Primeiro, vamos definir uma árvore infinita:
data Tree a = Tree (Tree a) a (Tree a)
instance Functor Tree where
fmap f (Tree l m r) = Tree (fmap f l) (f m) (fmap f r)
E então definiremos uma maneira de indexá-lo, para que possamos encontrar um nó com índice n
no tempo O (log n) :
index :: Tree a -> Int -> a
index (Tree _ m _) 0 = m
index (Tree l _ r) n = case (n - 1) `divMod` 2 of
(q,0) -> index l q
(q,1) -> index r q
... e podemos achar uma árvore cheia de números naturais conveniente, para que não tenhamos que mexer com esses índices:
nats :: Tree Int
nats = go 0 1
where
go !n !s = Tree (go l s') n (go r s')
where
l = n + s
r = l + s
s' = s * 2
Como podemos indexar, você pode converter uma árvore em uma lista:
toList :: Tree a -> [a]
toList as = map (index as) [0..]
Você pode verificar o trabalho até agora, verificando se isso toList nats
oferece[0..]
Agora,
f_tree :: Tree Int
f_tree = fmap (f fastest_f) nats
fastest_f :: Int -> Int
fastest_f = index f_tree
funciona exatamente como na lista acima, mas, em vez de levar um tempo linear para encontrar cada nó, pode persegui-lo em tempo logarítmico.
O resultado é consideravelmente mais rápido:
*Main> fastest_f 12380192300
67652175206
*Main> fastest_f 12793129379123
120695231674999
Na verdade, é muito mais rápido que você possa passar e substituir Int
com Integer
acima e obter ridiculamente grandes respostas quase que instantaneamente
*Main> fastest_f' 1230891823091823018203123
93721573993600178112200489
*Main> fastest_f' 12308918230918230182031231231293810923
11097012733777002208302545289166620866358