带有 memoization 的递归
递归函数可能会非常昂贵。如果它们是纯函数(当使用相同的参数调用时总是返回相同的值,并且既不依赖也不修改外部状态),通过存储已经计算的值,可以以牺牲内存为代价使它们更快。
以下是具有 memoization 的 Fibonacci 序列的实现:
#include <map>
int fibonacci(int n)
{
static std::map<int, int> values;
if (n==0 || n==1)
return n;
std::map<int,int>::iterator iter = values.find(n);
if (iter == values.end())
{
return values[n] = fibonacci(n-1) + fibonacci(n-2);
}
else
{
return iter->second;
}
}
请注意,尽管使用简单的递归公式,但在第一次调用时此函数为$ O(n)
$。在具有相同值的后续调用中,它当然是$ O(1)
$。
但请注意,此实现不可重入。此外,它不允许摆脱存储的值。另一种实现方式是允许将地图作为附加参数传递:
#include <map>
int fibonacci(int n, std::map<int, int> values)
{
if (n==0 || n==1)
return n;
std::map<int,int>::iterator iter = values.find(n);
if (iter == values.end())
{
return values[n] = fibonacci(n-1) + fibonacci(n-2);
}
else
{
return iter->second;
}
}
对于此版本,调用者需要使用存储的值维护映射。这样做的好处是该函数现在可以重入,并且调用者可以删除不再需要的值,从而节省内存。它的缺点是它破坏了封装; 调用者可以通过使用不正确的值填充映射来更改输出。