เราสามารถทำได้อย่างมีประสิทธิภาพโดยการสร้างโครงสร้างที่เราสามารถจัดทำดัชนีในเวลาเชิงเส้นย่อย
แต่แรก,
{-# LANGUAGE BangPatterns #-}
import Data.Function (fix)
มากำหนดf
กัน แต่ให้ใช้ 'open recursion' แทนที่จะเรียกตัวเองโดยตรง
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)
คุณสามารถรับ unmemoized 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