簡體   English   中英

在Haskell中記憶的最有效方法是什么?

[英]What's the most efficient way to memoize in Haskell?

在Haskell中記憶遞歸函數的最快方法是什么?

背景:最近我一直在Haskell中解決Project Euler問題。 許多需要許多遞歸定義的組合或數理論函數的計算,例如斐波那契數。 如果這些函數被記憶,性能會大大提高,也就是說,函數的結果會被緩存以供以后使用。

我已經看到很多解決這個問題的方法。 最優雅的似乎就是這個。 一個使用Data.IntMap(或哈希表)和State monad。 這個答案中提出了一個基於樹的解決方案,這種解決方案似乎很常見。 再舉一個例子,請參閱此博客文章 我見過其他使用內置函數的解決方案。 還有一個在第2節在這里fix ,並進一步似乎編譯器有時可以按摩到memoizing無需額外的工作。 還有一些預建解決方案

我想知道哪種memoization方法在Project Euler中使用的各種函數的實踐中最快。 我的直覺說哈希表庫是,因為哈希表似乎是命令式語言中首選的字典結構。 純粹的功能樹解決方案很酷,但我的谷歌搜索告訴我,它們在漸近性能方面嚴格比哈希表差。

編輯

一些評論說這個問題太寬泛而無法回答,經過反思我同意。 因此,讓我給出兩個具體的memoize函數示例:一個遞歸計算第n個Fibonacci數的函數,以及一個遞歸計算加泰羅尼亞數的函數。 我想為大n計算這些函數很多次。

我知道這些有明確的公式,但讓我們忽略它,因為這里的真正要點是使用它們來記錄備忘錄技術。

當試圖找到第n個斐波那契數時,你需要記住的唯一數字是前兩個數字。 你可以像(f n-1,fn)這樣的元組,並在每個循環上更新這個元組。 請注意,更新元組是通過指針操作完成的,並且計算成本不高。

一個更清潔,更智能的替代方案是:

fibs :: [Integer]
fibs = fibcreator 0 1
  where
    fibcreator a b = a : fibcreator b (a+b)

nth = take n fibs

但我見過的最好的算法之一是:

  1. 我們定義一個矩陣m = [e11 = 1,e12 = 1,e21 = 1,e22 = 0]
  2. 得到第n個斐波那契數,我們計算m'= m ^(n-1)
  3. 矩陣m'中的e11元素是第n個斐波納契數

現在最棒的是,為了獲得17個斐波納契數,我們可以做到

m' = ((((m^2)^2)^2)^2) * m

這顯着減少了計算時間並被動地將記憶嵌入算法中。 重點是Haskell已經使用這種算法來計算冪函數,因此您不需要實現它。 完整的實施是:

data Matrix = Matrix Integer Integer Integer Integer

instance Num Matrix where
  (*) (Matrix a11 a12 a21 a22) (Matrix b11 b12 b21 b22)
   = Matrix (a11*b11 + a12*b21) (a11*b12 + a12*b22) (a21*b11 + a22*b21) (a21*b12 + a22*b22)

fib4 :: Integer -> Integer
fib4 0 = 0
fib4 n = x
  where
    (Matrix x _ _ _) = Matrix 1 1 1 0 ^ (n-1)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM