【问题标题】:Optimizing a recursive counting algorithm优化递归计数算法
【发布时间】:2016-02-18 20:04:46
【问题描述】:

我讨厌在这里发帖寻求家庭作业帮助,但我已经用尽了我的能力。在用 C 语言编写一个简单的递归函数时,我遇到了 Stack Overflow(至少在 Java 中就是这样)问题。

我需要 n 个数字的每个 r 排列,并且我认为最好的方法是以 n 为底数到长度 r。

它适用于较小数量的数字,但最高的情况(n=10,r=6)最终会耗尽内存。我可以很容易地迭代地编写它,但它需要递归。这是我到目前为止所拥有的:

int permute(int *tempArray, int amtNums, int goalLength, int totalMatches) {
    totalMatches += 1; //Temporary, will be replaced by a function later

    printArray(tempArray, goalLength);

    tempArray[0]++;

    int j = 0;
    while(tempArray[j] >= amtNums) {
        tempArray[j+1]++;
        tempArray[j] = 0;
        j++;
    }

    if(j+1 > goalLength) return totalMatches;


    return permute(tempArray, amtNums, goalLength, totalMatches);
}  

在最大情况下被称为permute((int*)calloc(numSlots, sizeof(int)), 10, 6, 0);,n=10 r=6

我应该注意:计数并不完全直观,它有点倒退,但会生成我想要的所有数字组合。举个例子:n=4, r=3

0 0 0 
1 0 0 
2 0 0 
3 0 0 
0 1 0 
1 1 0 
2 1 0 
3 1 0 
.....
0 2 3 
1 2 3 
2 2 3 
3 2 3 
0 3 3 
1 3 3 
2 3 3 
3 3 3 

【问题讨论】:

  • 虽然这不是堆栈溢出的原因,但千万不要将calloc作为参数传递,你需要检查他的返回。
  • @AlterMann 我实际上并没有传递 calloc,我只是把它放在了问题中,所以我不必在问题中占用更多空间,将其视为伪代码
  • @gsamaras 它适用于不需要那么多递归的较小情况
  • @Duck:提示:你的调用栈不应该超过n deep。
  • @MooingDuck 我知道这是最好的方法,我只是不知道该怎么做。

标签: c algorithm recursion


【解决方案1】:

我相信您必须修改您的递归函数才能获得goalLength 的函数调用堆栈的最大深度。您可以像我在这里所做的那样添加参数depth

int permute( int *tempArray, int amtNums, int goalLength, int depth, int totalMatches) {
    int i;

    if ( depth < goalLength - 1) {
        for ( i = 0; i < amtNums; ++i ) {
            tempArray[depth] = i;
            totalMatches = permute(tempArray, amtNums, goalLength, depth + 1, totalMatches);
        }
    } else {
        for ( i = 0; i < amtNums; ++i ) {
            tempArray[depth] = i;
            printArray(tempArray, goalLength);
            ++totalMatches;
        }
    }

    return totalMatches;
}

您当然可以重写它,将for 循环放在外面,将if 放在里面。我用这个小测试程序尝试了该代码:

#include <stdio.h>

#define NDIGIT 4
#define NLENGTH 3

void printArray( int *temp, int size ) {
    int i;

    for ( i = 0; i < size; ++i ) {
        printf("%d ", temp[i]);
    }
    printf("\n"); 
}

int permute( int *tempArray, int amtNums, int goalLength, int depth, int totalMatches);

int main(void) {
    int results[NLENGTH];
    int n = permute(results, NDIGIT, NLENGTH, 0, 0);

    printf("Total number of permutations: %d\n", n);

    return 0;
}

NDIGIT 设置为10,将NLENGTH 设置为6 并注释掉打印功能(如果需要,可以保留它...)程序运行良好,输出为:

Total number of permutations: 1000000

【讨论】:

    【解决方案2】:

    您的函数对自身使用尾调用,编译器应该能够将此递归转换为循环。使用命令行选项 -O3 或您的 IDE 提供的任何内容启用优化。如果编译器实现了递归,函数会递归amtNumsgoalLength,对于n=10和r=6来说这是一个很大的数:1 000 000 递归级别可能会导致 堆栈溢出

    【讨论】:

    • 这听起来像是我不想使用的廉价解决方法。我想实际优化它以在没有-03 的任何系统上工作
    • 不要为每一步递归,想办法只在需要翻转下一位时递归。
    • 我不知道描述它的最佳方式,我可能在心里把 r 和 n 颠倒了,最大运行时间应该是 10^6 而不是 6^10。请参阅我对原始问题的编辑,了解我正在尝试使用长度 3、数量为 4 的示例
    • 这可行。希望这将足够“递归”。就我个人而言,我觉得对不应该递归完成的任务要求递归是愚蠢的。
    • 我确定了答案,递归 100 万次可能还是太多了:20 到 28 MB 之间,具体取决于架构。
    猜你喜欢
    • 2010-12-01
    • 2012-08-11
    • 2017-07-23
    • 1970-01-01
    • 2019-09-03
    • 2019-10-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多