C++ 用地图记忆

C++ 用地图记忆,c++,dictionary,recursion,data-structures,memoization,C++,Dictionary,Recursion,Data Structures,Memoization,我正在尝试使用map优化递归问题,以处理运行时错误。然而,使用记忆化方法和实现映射仍然不能完全解决这个问题。通过使用简单的递归方式,直到totalNum=36,分段错误才会出现。优化后,当totalNum变得非常大时,仍然会出现分段错误,如99999。 即使totalNum大于99999,有没有办法解决这个运行时问题?以下是我的头文件代码: using namespace std; static map<pair<int, int>, double> map; dou

我正在尝试使用map优化递归问题,以处理运行时错误。然而,使用记忆化方法和实现映射仍然不能完全解决这个问题。通过使用简单的递归方式,直到totalNum=36,分段错误才会出现。优化后,当totalNum变得非常大时,仍然会出现分段错误,如99999。 即使totalNum大于99999,有没有办法解决这个运行时问题?以下是我的头文件代码:

using namespace std; 
static map<pair<int, int>, double> map;

double val(int r, int b){   
if (0 == r)
    return ((double) b);
if (0 == b)
    return (0);
if (map.find(make_pair(r, b)) != map.end()){
    return (double)map[make_pair(r, b)];
} else{
    double num1 = ((double) r/(r+b)) * value(r-1, b);
    double num2 = ((double) b/(r+b)) * value(r, b-1);
    double value = max((num1 + num2), (double) (b - r));
    map[make_pair(r, b)] = value;
    return value;
}
}

#endif
使用名称空间std;
静态地图;
双val(intr,intb){
如果(0==r)
返回((双)b);
如果(0==b)
返回(0);
if(map.find(make_pair(r,b))!=map.end(){
返回(双)映射[make_pair(r,b)];
}否则{
双num1=((双)r/(r+b))*值(r-1,b);
双num2=((双)b/(r+b))*值(r,b-1);
双倍值=最大值((num1+num2),(双倍)(b-r));
map[make_pair(r,b)]=值;
返回值;
}
}
#恩迪夫
它在main()中用于打印消息:cout优化(以及作为优化技术的记忆)用于将缓慢的代码转换为快速的代码,而不是将容易出错的代码转换为无错误的代码

很可能你的segfault(见wiki)与备忘录没有任何关系。当然,添加记忆改变了segfault的频率和行为,但这并没有导致它,也没有删除它

segfault最常见的原因是试图使用一个释放的(
delete
d in C++)指针,但是您的代码没有直接处理任何原始指针,因此可能这不是原因

SEGFULTS的第二个最常见的原因是返回对局部变量的引用,这种情况也不会发生

然而,因为在记忆之后,segfault发生在一个更大的数字(totalNum)上,它可能与堆栈大小有关。在C++中,递归通常导致堆栈使用量不断增加,并且您可以轻松地使用操作系统通常提供的所有堆栈(通常会发出堆栈溢出信号,这是一个Sebug)。这可能是分割错误的根源。如果您在linux上,请尝试使用
ulimit-s
查看您当前的堆栈大小,并对64 Mb的堆栈使用类似于
ulimit-s 65536
的方法来粗暴地增加它,以查看这是否会使您的totalNum更大。如果是这样,那么您现在知道您的问题是堆栈溢出。因此,无错误的解决方案是将递归算法更改为基于任务的算法(即,设置一个要处理的值队列,循环并处理其中的项,而不是使用递归)

否则,最好有更多的代码来查看,因为segfault不太可能是由该记忆代码引起的

另外,如果您提供了一个最小的工作示例,其中包含一个伪主函数和编译所需的所有内容,那将是非常好的

即使totalNum大于99999,有没有办法解决这个运行时问题? 摆脱递归。计算值
(r,b)
,很容易找出需要哪些值。您可以使用循环来计算它们

递归版本的问题在于它需要大量的堆栈空间,这是非常有限的。因此,此解决方案无法扩展。记忆化并没有改变这一点,因为函数参数和返回值仍然需要保存在堆栈上。即使递归可以优化(通常只有尾部递归),未优化的构建仍然不会运行。这可能意味着您无法有效地调试程序


当您更改为循环时,可以将堆栈空间重新用于函数参数,并可以将结果存储在堆上。

使用调试器逐步检查代码,并确定
映射是否是真正的罪魁祸首。旁白:您使用了
名称空间std'ed,然后在紧接着的下一行中引入了命名冲突
map
。请不要这样做。您可以将地图移动到
vals
,并定义它
static std::map memo许多编译器都有设置堆栈大小的选项。看看
记忆化并没有改变这一点,因为函数参数和返回值仍然需要保存在堆栈上。
我认为这是误导。比如说,函数A自身需要10次函数调用来计算输入X,如果输入X已经缓存了结果,则记忆版本大约需要1次函数调用。这可能会对所使用的堆栈空间产生影响——或者不产生影响,这取决于通常调用函数a的方式/时间。@GabrielFrancischini我的意思是,记忆化不会产生根本性的影响;递归方法仍然不能扩展。据此编辑。