Możemy to zrobić bardzo efektywnie, tworząc strukturę, którą możemy indeksować w czasie nieliniowym.
Ale najpierw,
{-# LANGUAGE BangPatterns #-}
import Data.Function (fix)
Zdefiniujmy f
, ale niech używa „otwartej rekursji” zamiast wywoływać się bezpośrednio.
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)
Możesz uzyskać niezapamiętać f
, używającfix f
Pozwoli ci to sprawdzić, f
czy to, co masz na myśli, dla małych wartości f
, dzwoniąc, na przykład:fix f 123 = 144
Moglibyśmy to zapamiętać, definiując:
f_list :: [Int]
f_list = map (f faster_f) [0..]
faster_f :: Int -> Int
faster_f n = f_list !! n
To działa dość dobrze i zastępuje to, co miało zająć O (n ^ 3) czas, czymś, co zapamiętuje wyniki pośrednie.
Ale wciąż potrzeba czasu liniowego, aby indeksować, aby znaleźć zapamiętaną odpowiedź mf
. Oznacza to, że wyniki takie jak:
*Main Data.List> faster_f 123801
248604
są do zaakceptowania, ale wynik nie skaluje się dużo lepiej. Możemy zrobić lepiej!
Najpierw zdefiniujmy nieskończone drzewo:
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)
Następnie zdefiniujemy sposób indeksowania do niego, abyśmy mogli zamiast tego znaleźć węzeł z indeksem n
w czasie 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
... i możemy znaleźć drzewo pełne liczb naturalnych dla wygody, więc nie będziemy musieli bawić się tymi indeksami:
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
Ponieważ możemy indeksować, możesz po prostu przekonwertować drzewo na listę:
toList :: Tree a -> [a]
toList as = map (index as) [0..]
Możesz sprawdzić dotychczasową pracę, weryfikując, która toList nats
daje[0..]
Teraz,
f_tree :: Tree Int
f_tree = fmap (f fastest_f) nats
fastest_f :: Int -> Int
fastest_f = index f_tree
działa tak samo, jak w przypadku powyższej listy, ale zamiast potrzebować czasu liniowego na znalezienie każdego węzła, można go ścigać w czasie logarytmicznym.
Wynik jest znacznie szybszy:
*Main> fastest_f 12380192300
67652175206
*Main> fastest_f 12793129379123
120695231674999
W rzeczywistości jest to o wiele szybsze, że możesz przejść przez powyższe i zastąpić Int
je Integer
powyższymi i niemal natychmiast uzyskać absurdalnie duże odpowiedzi
*Main> fastest_f' 1230891823091823018203123
93721573993600178112200489
*Main> fastest_f' 12308918230918230182031231231293810923
11097012733777002208302545289166620866358