Możemy to zrobić bardzo skutecznie, tworząc strukturę, którą możemy indeksować w czasie podliniowym.
Ale najpierw
{-# LANGUAGE BangPatterns #-}
import Data.Function (fix)
Zdefiniujmy f
, ale sprawiają, że korzystać z „otwartą” rekursji zamiast nazywać 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żna uzyskać unmemoized f
za pomocą fix f
To pozwoli Ci przetestować że f
robi to, co oznacza dla małych wartości f
wywołując na przykład: fix f 123 = 144
Mogliśmy memoize to przez zdefiniowanie:
f_list :: [Int]
f_list = map (f faster_f) [0..]
faster_f :: Int -> Int
faster_f n = f_list !! n
Który wykonuje się dobrze i dobrze asy, co miało zabrać czas na coś, co zapamiętuje wyniki pośrednie.
Ale wciąż trwa liniowy czas tylko po to, aby znaleźć indeksowaną odpowiedź dla mf
. Oznacza to, że wyniki takie jak:
*Main Data.List> faster_f 123801
248604
są dopuszczalne, ale wynik nie jest o wiele lepszy. Możemy zrobić lepiej!
Najpierw określić nieskończoną drzewa:
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)
A potem będziemy definiować sposób indeksu do niego, więc możemy znaleźć węzła o indeksie n
w O (log n) czasie zamiast:
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, aby być wygodne, więc nie trzeba bawić się wokół tych indeksów:
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 indeksu, można po prostu przekonwertować drzewa na listę:
toList :: Tree a -> [a]
toList as = map (index as) [0..]
można sprawdzić pracę dotychczas przez weryfikację że 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 jak z powyższą listą, ale zamiast brać liniowy czas na znalezienie każdego węzła, może go ścigać w logarytmicznym czasie.
Rezultatem jest znacznie szybsze:
*Main> fastest_f 12380192300
67652175206
*Main> fastest_f 12793129379123
120695231674999
W rzeczywistości jest to o wiele szybciej, że można przejść i zastąpić Int
z Integer
powyżej dostać absurdalnie duże odpowiedzi niemal natychmiast
*Main> fastest_f' 1230891823091823018203123
93721573993600178112200489
*Main> fastest_f' 12308918230918230182031231231293810923
11097012733777002208302545289166620866358
Czy to zadanie domowe? –
Tylko w tym sensie, że jest to praca, którą wykonuję w domu :-) –