一種對動態編程非常有用的技術叫做記憶。有關更多詳細信息,請參閱例如blog post by Don Syme或introduction by Matthew Podwysocki。
這個想法是,你寫(一個天真的)遞歸函數,然後添加緩存,存儲以前的結果。這使您可以使用通常的函數式編寫函數,但可以使用動態編程實現算法的性能。
例如,對於計算Fibonacci數天真的(低效率)的功能如下:
let rec fibs n =
if n < 1 then 1 else
(fibs (n - 1)) + (fibs (n - 2))
這是低效的,因爲當你調用fibs 3
,它會調用fibs 1
三次(和許多倍,如果你打電話,例如,fibs 6
)。 memoization背後的想法是我們編寫一個緩存,其中存儲fib 1
和fib 2
的結果等,因此重複調用將從緩存中選擇預先計算的值。
一個通用的函數,它的記憶化可以這樣寫:
open System.Collections.Generic
let memoize(f) =
// Create (mutable) cache that is used for storing results of
// for function arguments that were already calculated.
let cache = new Dictionary<_, _>()
(fun x ->
// The returned function first performs a cache lookup
let succ, v = cache.TryGetValue(x)
if succ then v else
// If value was not found, calculate & cache it
let v = f(x)
cache.Add(x, v)
v)
編寫更有效的斐波那契的功能,我們就可以調用memoize
,並給它執行計算作爲參數的函數:
let rec fibs = memoize (fun n ->
if n < 1 then 1 else
(fibs (n - 1)) + (fibs (n - 2)))
請注意,這是一個遞歸值 - 該函數的主體調用memnalized fibs
函數。
這往往是一個更容易閱讀memoized(遞歸)動態程序設計問題與程序化程序設計問題相比,但是可能更容易推斷程序版本的運行時間。 – gradbot