递归在C++中确实很慢

Recursion is really that slow in C++?

本文关键字:C++ 递归      更新时间:2023-10-16

我知道递归在某些语言中很快(甚至比迭代更快)。但我说的是C++(也许C也是一样的)。

我正在解决一个在线评委问题(不是家庭作业)。我使用自上而下的动态编程方法解决了这个问题。但我最后得了两个TLE。然后,我使用了自下而上的方法,解决方案被接受了。然而,在这个问题中,我认为自上而下应该更快,因为可以进行优化来跳过一些不需要计算的状态。

问题说明:http://www.hkoi.org/training2004/files/need-for-speed.pdf

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
int n, max_t; 
int num[51][50001];
int ts[50][50];
int ms[50];
int ls[50];
#ifdef RECURSION
int solve(int rn, int tleft)
{
    if (rn >= n)
        return 0;
    if (num[rn][tleft] != -1)
        return num[rn][tleft];
    int t;
    int max_num = solve(rn + 1, tleft);
    for (int i = rn; i < n; ++i)
    {
        t = ls[i];
        for (int j = 0; j < ms[i]; ++j)
        {
            t += ts[i][j];
            if (t > tleft)
                break;
            max_num = max(max_num, solve(i + 1, tleft - t) + j + 1);
        }
    }
    num[rn][tleft] = max_num;
    return max_num;
}
#endif
int main()
{
    scanf("%d %d", &n, &max_t);
    for (int i = 0; i < n; ++i)
    {
        scanf("%d %d", ls + i, ms + i);
        for (int j = 0; j < ms[i]; ++j)
        {
            scanf("%d", ts[i] + j);
        }
        sort(ts[i], ts[i] + ms[i]);
    }
    for (int i = 0; i <= n; ++i)
    {
        for (int j = 0; j <= max_t; ++j)
        {
            #ifdef RECURSION
            num[i][j] = -1;
            #else
            num[i][j] = 0;
            #endif
        }
    }
    #ifdef RECURSION
    printf("%dn", solve(0, max_t));
    #endif
    #ifndef RECURSION
    int t;
    for (int i = n - 1; i >= 0; --i)
    {
        for (int j = 0; j <= max_t; ++j)
        {
            t = ls[i];
            num[i][j] = num[i + 1][j];
            for (int k = 0; k < ms[i]; ++k)
            {
                t += ts[i][k];
                if (t > j)
                    break;
                num[i][j] = max(num[i][j], num[i + 1][j - t] + k + 1);
            }
        }
    }
    printf("%dn", num[0][max_t]);
    #endif
}

正如您在递归版本中看到的,有一行max_num = max(max_num, solve(i + 1, tleft - t) + j + 1);。由于t增加了一个整数,但并不总是=1,因此跳过了某些情况。然而,在迭代版本中,会计算出许多无用的状态。

我认为,如果迭代解决方案更快,那么递归必须比迭代慢得多。我的说法正确吗?

性能优化是一个非常复杂的领域,通常不能将其缩小为简单的建议,例如"迭代比递归快"。

反例发现得很快:例如,大多数最快的排序算法(如Quicksort)通常是递归实现的。

在您的情况下,在实践中,过于频繁地执行一些廉价的操作可能比执行复杂的检查来防止它们快得多,例如,因为缓存未命中较少。判断一个解决方案是否优于另一个的唯一方法是使用探查器进行仔细分析。

看看这个涵盖分支预测的优秀答案(实际上它更像一篇文章):为什么处理排序数组比处理未排序数组更快?

理论上迭代==递归,假设您不使用编译器优化。主要区别在于堆栈中充满了使用递归解决方案的函数调用。然而,递归通常更容易转换为多线程应用程序。此外,如果你有很多if语句,递归解决方案有助于使代码更加抽象,并且你可以在执行过程中非常容易地切换到另一种类型的递归(只需以不同的方式调用函数或调用完全不同的函数)。