我该如何解决这种递归溢出?

时间:2016-06-16 16:31:19

标签: c algorithm memory recursion

#define MIN -2147483648


long max(long x,long y)
{

    long m=x;
    if(y>x)
        m=y;
    return m;
}

long f(int x,int y,int **p)
{

    long result;
    if(x<0||y<0)
        result = MIN;
    else 
        if(x==0&&y==0)
            result = p[0][0];
        else
            result = max(f(x-1,y,p),f(x,y-1,p))+p[x][y];
    return result; 
}

int main(void)
{

    int n;
    scanf("%d",&n);
    int** p = (int **)malloc(n*sizeof(int*));
    for(int i=0;i<n;i++)
    {
        p[i] = (int*)malloc(n*sizeof(int));
        for(int j=0;j<n;j++)    
            scanf("%d",p[i]+j);
    }
    printf("haha\n");
    printf("%ld\n",f(n-1,n-1,p));   
    return 0;
}

当我将10分配给n时,效果很好。但是当我将{20}分配给n时,没有结果。我用谷歌搜索它,我猜测错误可能是递归溢出。那么我该如何解决这个问题呢?

2 个答案:

答案 0 :(得分:3)

你正在进行大量的递归调用。在每个级别,您将调用次数设置为先前级别的两倍。因此,当N为20时,您正在进行2 ^ 20 = 1048576个函数调用。这需要很长时间。

这些调用中的大多数都会反复重新计算相同的值。而是重新计算这些值,只计算一次。

这是一种非递归的方法:

long f(int x,int y,int **p)
{
    long **p2;
    int i, j;

    p2 = malloc(sizeof(long *)*(x+1));
    for (i=0;i<=x;i++) {
        p2[i] = malloc(sizeof(long)*(y+1));
        for (j=0;j<=y;j++) {
            if (i==0 && j==0) {
                p2[i][j] = p[i][j];
            } else if (i==0) {
                p2[i][j] = p2[i][j-1] + p[i][j];
            } else if (j==0) {
                p2[i][j] = p2[i-1][j] + p[i][j];
            } else {
                p2[i][j] = max(p2[i-1][j], p2[i][j-1]) + p[i][j];
            }
        }
    }
    return p2[x][y];
}

编辑:

如果您仍想要递归解决方案,则可以执行以下操作。如果尚未计算必要的值,则仅进行递归调用。

long f(int x,int y,int **p)
{
    static long**p2=NULL;
    int i, j;

    if (!p2) {
        p2 = malloc(sizeof(long*)*(x+1));
        for (i=0;i<=x;i++) {
            p2[i] = malloc(sizeof(long)*(y+1));
            for (j=0;j<=y;j++) {
                p2[i][j] = MIN;
            }
        }
    }

    if (x==0 && y==0) {
        p2[x][y] = p[x][y];
    } else if (x==0) {
        if (p2[x][y-1] == MIN) {
            p2[x][y-1] = f(x,y-1,p);
        }
        p2[x][y] = p2[x][y-1] + p[x][y];
    } else if (y==0) {
        if (p2[x-1][y] == MIN) {
            p2[x-1][y] = f(x-1,y,p);
        }
        p2[x][y] = p2[x-1][y] + p[x][y];
    } else {
        if (p2[x][y-1] == MIN) {
            p2[x][y-1] = f(x,y-1,p);
        }
        if (p2[x-1][y] == MIN) {
            p2[x-1][y] = f(x-1,y,p);
        }
        p2[x][y] = max(p2[x-1][y], p2[x][y-1]) + p[x][y];
    }

    return p2[x][y];
}

答案 1 :(得分:0)

您没有指定使用的编译器。了解如何增加程序的堆栈大小。但是,即使你让n = 20工作,由于前面评论中提到的组合爆炸,也会有一个限制(可能离n = 20不远)。

对于n&gt; 0,每次调用f(n)调用f(n-1)两次。所以调用f(n)=调用2 * fn(n-1)

对于n = 20,即2 ^ 20个呼叫。每次调用返回一个long。如果long是8个字节= 2 ^ 3,那么堆栈上至少有2 ^ 23个字节。

修改

实际上,根据documentation,链接器控制堆栈大小。 您可以尝试增加堆栈大小并实现更高效的算法,如不同的答案所提出的

使用ld(GNU链接器)增加堆栈大小

- 筹码储备

- 堆栈保留,提交

指定要保留(并可选择提交)的内存字节数,以用作此程序的堆栈。默认值为2Mb保留,4K已提交。 [此选项特定于链接器的i386 PE目标端口]