Chúng ta có thể làm điều này rất hiệu quả bằng cách tạo ra một cấu trúc mà chúng ta có thể lập chỉ mục trong thời gian tuyến tính phụ.
Nhưng trước tiên,
{-# LANGUAGE BangPatterns #-}
import Data.Function (fix)
Hãy xác định f
, nhưng làm cho nó sử dụng 'đệ quy mở' thay vì gọi trực tiếp.
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)
Bạn có thể nhận được một unmemoized f
bằng cách sử dụngfix f
Điều này sẽ cho phép bạn kiểm tra f
xem ý nghĩa của bạn đối với các giá trị nhỏ f
bằng cách gọi, ví dụ:fix f 123 = 144
Chúng ta có thể ghi nhớ điều này bằng cách định nghĩa:
f_list :: [Int]
f_list = map (f faster_f) [0..]
faster_f :: Int -> Int
faster_f n = f_list !! n
Điều đó thực hiện rất tốt, và thay thế những gì sẽ mất thời gian O (n ^ 3) bằng một cái gì đó ghi nhớ các kết quả trung gian.
Nhưng nó vẫn mất thời gian tuyến tính chỉ để lập chỉ mục để tìm câu trả lời ghi nhớ cho mf
. Điều này có nghĩa là kết quả như:
*Main Data.List> faster_f 123801
248604
có thể chấp nhận được, nhưng kết quả không có quy mô tốt hơn thế nhiều. Chúng ta có thể làm tốt hơn!
Đầu tiên, hãy xác định một cây vô hạn:
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)
Và sau đó chúng ta sẽ xác định một cách để lập chỉ mục vào nó, vì vậy chúng ta có thể tìm thấy một nút có chỉ mục n
trong thời gian 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
... Và chúng ta có thể tìm thấy một cây chứa đầy số tự nhiên để thuận tiện vì vậy chúng ta không phải loay hoay với những chỉ số đó:
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
Vì chúng tôi có thể lập chỉ mục, bạn chỉ có thể chuyển đổi một cây thành một danh sách:
toList :: Tree a -> [a]
toList as = map (index as) [0..]
Bạn có thể kiểm tra công việc cho đến nay bằng cách xác minh toList nats
cung cấp cho bạn[0..]
Hiện nay,
f_tree :: Tree Int
f_tree = fmap (f fastest_f) nats
fastest_f :: Int -> Int
fastest_f = index f_tree
hoạt động giống như với danh sách ở trên, nhưng thay vì mất thời gian tuyến tính để tìm từng nút, có thể đuổi nó xuống theo thời gian logarit.
Kết quả nhanh hơn đáng kể:
*Main> fastest_f 12380192300
67652175206
*Main> fastest_f 12793129379123
120695231674999
Trong thực tế, nó nhanh hơn rất nhiều mà bạn có thể đi qua và thay thế Int
bằng Integer
ở trên và nhận được câu trả lời lớn một cách lố bịch gần như ngay lập tức
*Main> fastest_f' 1230891823091823018203123
93721573993600178112200489
*Main> fastest_f' 12308918230918230182031231231293810923
11097012733777002208302545289166620866358