Kita dapat melakukannya dengan sangat efisien dengan membuat struktur yang dapat kita indeks dalam waktu sub-linier.
Tapi pertama-tama,
{-# LANGUAGE BangPatterns #-}
import Data.Function (fix)
Mari kita definisikan f
, tetapi gunakan 'rekursi terbuka' daripada memanggil dirinya sendiri secara langsung.
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)
Anda bisa mendapatkan keliru f
dengan menggunakanfix f
Ini akan memungkinkan Anda menguji f
apakah yang Anda maksud untuk nilai-nilai kecil f
dengan memanggil, misalnya:fix f 123 = 144
Kita bisa mengingat ini dengan mendefinisikan:
f_list :: [Int]
f_list = map (f faster_f) [0..]
faster_f :: Int -> Int
faster_f n = f_list !! n
Itu berkinerja cukup baik, dan menggantikan apa yang akan memakan waktu O (n ^ 3) dengan sesuatu yang mengingat hasil antara.
Tetapi masih membutuhkan waktu linier hanya untuk mengindeks untuk menemukan jawaban yang dimo mf
. Artinya hasil seperti:
*Main Data.List> faster_f 123801
248604
dapat ditoleransi, tetapi hasilnya tidak berskala lebih baik dari itu. Kami bisa lebih baik!
Pertama, mari kita definisikan pohon tak terbatas:
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)
Dan kemudian kita akan menentukan cara untuk mengindeks ke dalamnya, sehingga kita dapat menemukan simpul dengan indeks n
dalam waktu 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
... dan kami mungkin merasa nyaman menggunakan pohon yang penuh dengan bilangan asli, jadi kami tidak perlu bermain-main dengan indeks tersebut:
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
Karena kami dapat mengindeks, Anda dapat mengubah pohon menjadi daftar:
toList :: Tree a -> [a]
toList as = map (index as) [0..]
Anda dapat memeriksa pekerjaan sejauh ini dengan memverifikasi yang toList nats
memberi Anda[0..]
Sekarang,
f_tree :: Tree Int
f_tree = fmap (f fastest_f) nats
fastest_f :: Int -> Int
fastest_f = index f_tree
bekerja seperti daftar di atas, tetapi alih-alih mengambil waktu linier untuk menemukan setiap node, dapat mengejarnya dalam waktu logaritmik.
Hasilnya jauh lebih cepat:
*Main> fastest_f 12380192300
67652175206
*Main> fastest_f 12793129379123
120695231674999
Faktanya, ini jauh lebih cepat sehingga Anda dapat melalui dan mengganti Int
dengan di Integer
atas dan mendapatkan jawaban yang sangat besar hampir secara instan
*Main> fastest_f' 1230891823091823018203123
93721573993600178112200489
*Main> fastest_f' 12308918230918230182031231231293810923
11097012733777002208302545289166620866358