【问题标题】:Optimizing a recursive function优化递归函数
【发布时间】:2016-01-09 16:29:47
【问题描述】:

我正在创建一个程序,该程序仅使用 1、2、6 和 13 返回获得数字 (n) 所需的最少数量的总和。它非常适用于 n 的小值,但一旦 n 得到值像 200 一样,程序计算结果需要太多时间。

因此,我有两个问题:

1.有什么方法可以让递归更快?

2.我应该避免使用递归而使用循环吗?

这是注释代码:

#include <iostream>
#define MAX 500000

using namespace std;

void cal(int inp, int &mini, int counter = 0);

int main (void)
{
    //Gets input
    int n;
    cin >> n;

    //Defines mini as the MAX result we can get
    int mini = MAX;

    //Calls the function
    cal(n, mini);

    //Prints the best result
    cout << mini << endl;

    return 0;
}

void cal(int inp, int &mini, int counter)
{
    //Breaks recursion if it finds an answer
    if(!inp)
    {
        if(counter<mini) mini = counter;
        return;
    }

    //Breaks recursion if the input is negative
    //or the counter is more than the best result
    else if((inp<0) || (counter>mini)) return;

    //Counts amount of recursions
    counter++;

    //Tries every combination
    cal(inp-13, mini, counter);
    cal(inp-6, mini, counter);
    cal(inp-2, mini, counter);
    cal(inp-1, mini, counter);

    return;
}

谢谢

【问题讨论】:

  • 循环几乎总是比递归快。
  • 性能问题不是因为递归。这是因为你的蛮力算法。
  • 您需要渐近更好的算法,而不是微优化。
  • 不要使用递归。尽量减少函数调用/分支的数量。大多数处理器可以比分支指令更快地处理数据和移动指令。分支可能会导致处理器清空其指令流水线并重新加载或通过分支预测算法。无论哪种情况,这都是处理器可以用于其他目的的时间。所以,尽量减少递归调用。

标签: c++ function recursion optimization


【解决方案1】:

问题在于你的蛮力。让我提出更好的建议:

预备:如果你有两个 1,最好用一个 2。如果你有三个 2,最好用一个 6。如果你有十三个 6,最好用六个十三。

因此,任何可接受的总和总是看起来像 n = 13m+k,其中 k 写为 1、2 和 6 的总和。通过初步计算,我们知道最佳总和 k 永远不会超过 @ 987654324@。 (反过来不成立。当然,没有 78 的任何数字最好不要写 13。)所以蛮力强迫这些就足够了。然后,您可以使用查找表。

这仍然可以进一步优化,但不应在 200 时减速。

假设您找到了前 77 个条目(也可以优化),您可以这样做(仍然未优化 ;-):

int num_13 = ((n-78) / 13) + 1;
int sum_length = MAX;
for (i = num_13; i*13 < n; i++) {
    int tmp = entries_77[n-i*13]+i;
    if (tmp < sum_length) {
        num_13 = i;
        sum_length = tmp;
    }
}

我会更快地为等价类模 13 编译一个数组,因为对于任何给定的等价类,任何超过 78 的数字都将具有相同的 k

【讨论】:

    【解决方案2】:

    您可以使用 DP(动态编程)方法来解决您的问题。众所周知Coins Problem

    【讨论】:

    • 绝妙的答案,绝对是最优雅的答案。谢谢!
    • @Just_a_newbie 很高兴如果它有帮助))
    【解决方案3】:

    您的递归需要记忆以避免重复计算。并且不需要递归的第二个和第三个参数。我已经更新并解释了您的代码。如果您有任何困惑,请告诉我。

    #include <iostream>
    #include <string.h>
    #define INF 999999
    
    using namespace std;
    
    int cal(int inp);
    int mem[502];
    int main (void)
    {
        //Gets input
        int n;
        cin >> n;
    
        //initialzing the array for using with memoization
        memset(mem,-1,sizeof(mem));
    
        //Calls the function
        //Prints the best result
        cout << cal(n) << endl;
    
        return 0;
    }
    
    //returns the minimum quantity of sum operations to get inp.
    int cal(int inp)
    {
        //Breaks recursion if it finds an answer.
        //Return cost 0. As in this stage no processing was done.
        if(!inp)
            return 0;
    
        // Returning infinite cost for invalid case.
        if(inp < 0)
            return INF;
    
        int _ret = mem[inp];
    
        // If already visited here before then no need to calcuate again.
        // Just return previous calculation. This is called memoisation.
        // If not visited then _ret would have equal to -1.
        if(_ret >=0 )
            return _ret;
    
        _ret = INF;
    
        //Tries every combination and takes the minimum cost.
        _ret = min(_ret, cal(inp-13)+1);
        _ret = min(_ret,cal(inp-6)+1);
        _ret = min(_ret,cal(inp-2)+1);
        _ret = min(_ret,cal(inp-1)+1);
    
        // Updating the value so that can be used for memoization.
        mem[inp] = _ret;
    
        return _ret;
    }
    

    这也适用于更大的数字。复杂度为 4*n。

    【讨论】:

    • 谢谢,这给了我另一个解决问题的方法。绝对是我会不时应用的一种方法。
    猜你喜欢
    • 1970-01-01
    • 2010-10-20
    • 2018-03-08
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    • 1970-01-01
    相关资源
    最近更新 更多