Ми можемо зробити це дуже ефективно, створивши структуру, яку ми можемо індексувати в підлінійний час.
Але спочатку,
{-# LANGUAGE BangPatterns #-}
import Data.Function (fix)
Давайте визначимось f
, але змусимо використовувати «відкриту рекурсію», а не викликати себе безпосередньо.
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)
Ви можете не помітити f
їх, скориставшисьfix f
Це дозволить вам перевірити, що f
робить те, що ви маєте на увазі для малих значень f
, зателефонувавши, наприклад:fix f 123 = 144
Ми могли б запам'ятати це, визначивши:
f_list :: [Int]
f_list = map (f faster_f) [0..]
faster_f :: Int -> Int
faster_f n = f_list !! n
Це добре проходить і замінює те, що збиралося зайняти час O (n ^ 3) тим, що запам'ятовує проміжні результати.
Але все ще потрібен лінійний час, щоб просто проіндексувати, щоб знайти запам'ятоване відповідь mf
. Це означає, що такі результати:
*Main Data.List> faster_f 123801
248604
є терпимими, але результат не набагато кращий за це. Ми можемо зробити краще!
Спочатку давайте визначимо нескінченне дерево:
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)
І тоді ми визначимо спосіб індексувати його, щоб ми могли знайти вузол з індексом n
у час 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
... і ми можемо знайти дерево, повне природних чисел, зручним, тому нам не доведеться спіткнутися з цими показниками:
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
Оскільки ми можемо індексувати, ви можете просто перетворити дерево у список:
toList :: Tree a -> [a]
toList as = map (index as) [0..]
Ви можете перевірити роботу поки що, переконавшись, що toList nats
вам дає[0..]
Тепер,
f_tree :: Tree Int
f_tree = fmap (f fastest_f) nats
fastest_f :: Int -> Int
fastest_f = index f_tree
працює так само, як у списку вище, але замість того, щоб витрачати лінійний час на пошук кожного вузла, можна переслідувати його в логарифмічний час.
Результат значно швидший:
*Main> fastest_f 12380192300
67652175206
*Main> fastest_f 12793129379123
120695231674999
Насправді це набагато швидше, ніж ви можете пройти і замінити Int
з Integer
вище і отримати сміховинно великі відповіді майже миттєво
*Main> fastest_f' 1230891823091823018203123
93721573993600178112200489
*Main> fastest_f' 12308918230918230182031231231293810923
11097012733777002208302545289166620866358