一种对动态编程非常有用的技术叫做记忆。有关更多详细信息,请参阅例如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