【问题标题】:Can I set up this recursion in a smarter way to avoid calls to an expensive function?我可以以更智能的方式设置此递归以避免调用昂贵的函数吗?
【发布时间】:2014-06-12 18:37:36
【问题描述】:

假设我有一个矩阵类Matrix,以及一个使用它进行一些计算的递归函数。特别是,heavy_computation 是一个非常昂贵的函数,我想尽可能避免调用它。

bool heavy_computation(const Matrix& m)
{
    // Expensive stuff ...
}

do_things 函数是用户调用以启动递归的顶级函数。

void do_things(const Matrix& m, std::vector<int>& collection)
{
    std::vector<int> expensive_result;

    if(!heavy_computation(m, expensive_result))
    {
        return;
    }

    std::cout << "New result = { ";
    for(int i : expensive_result)
        std::cout << i << " ";
    std::cout << "}\n";

    for(int i : expensive_result)
    {
        collection.emplace_back(i);
    }

    for(int j = 0; j < 3; ++j)
    {
        explore(m, j, expensive_result, collection);
    }
}

顶级函数将工作委托给以下explore 函数。

void explore(
    const Matrix& m,
    int j,
    const std::vector<int>& expensive_result,
    std::vector<int>& collection)
{
    std::cout << "j = " << j << "\n";

    // Compute something based on expensive_result.
    Matrix new_matrix = ...;

    std::vector<int> new_expensive_result;
    heavy_computation(new_matrix , new_expensive_result);

    if (!new_expensive_result.empty())
    {
        std::cout << "New result = { ";
        for (int i : new_expensive_result)
            std::cout << i << " ";
        std::cout << "}\n";

        for (int i : new_expensive_result)
        {
            collection.emplace_back(i);
        }
    }

    for (int i = 0; i < 3; ++i)
    {
        if (!new_expensive_result.empty())
        {
            explore(new_matrix, i, expensive_result, collection);
        }
    }
}

我想我已经盯着递归的结构太久了;它很可能以更聪明的方式组织起来,以避免对heavy_computation 的不必要调用。例如,考虑程序给出的以下输出:

New result = { 9 7 8 }
j = 0
New result = { 5 0 6 }
j = 0
j = 1
j = 2
New result = { 3 1 0 }
j = 0
j = 1
j = 2
j = 1                     <--- Oh no, { 5 0 6 } will be recomputed again?!
New result = { 5 0 6 }
j = 0
j = 1
j = 2
New result = { 3 1 0 }
j = 0
j = 1
j = 2
j = 2
New result = { 5 0 6 }
j = 0
j = 1
j = 2

如果我理解正确,这里的问题是当explore 返回时,heavy_computation 可以再次被调用(当然它会再次返回相同的答案,因为它是确定性的)。基本上,如果heavy_computation 给出错误,explore 的一个分支将被杀死,否则递归会更深入。有没有办法设置do_thingsexplore 以避免不必要地调用昂贵的函数?

与输出相比,我想 3 次调用昂贵的函数就足够了,因为我们没有更多独特的答案。

【问题讨论】:

  • 我没有详细看你的代码结构,但是memoization在这里合适吗?
  • 如果这是您代码的输出,则意味着heavy_computation 是有状态的(前两个调用获得相同的输入,但产生不同的输出)。所以,要么你不看那个函数的作用就无法优化它,要么你没有发布你的实际代码浪费了我的时间。

标签: c++ recursion


【解决方案1】:

首先,您可以在代码中改进一些内容,使其更易于阅读和维护。你为什么将j 传递给explore 函数?您仅将其用于打印,因此请在调用该函数之前执行此操作。

第二,这个

for (int i : new_expensive_result)
{
    collection.emplace_back(i);
}

相同(除了更慢)
collection.insert(collection.end(),
    new_expensive_result.begin(), new_expensive_result.end());

第三,你的两个函数之间有很多代码重复。您应该在此处发布代码之前将其删除。

确实,解决您的问题非常简单。首先,将explore 函数拆分为两个函数:一个包含代码到heavy_computation 并包括对heavy_computation 的调用,另一个用于其余函数。递归调用现在将包含两个调用,您必须将第一个函数的一些输出传递给第二个函数。类似的东西

for (int i = 0; i < 3; ++i)
{
    if (!new_expensive_result.empty())
    {
        std::cout << "j = " << i << "\n";

        Matrix matrix_result;
        std::vector<int> vector_result;
        explore1(new_matrix, expensive_result, matrix_result, vector_result);
        explore2(new_matrix, expensive_result, matrix_result, vector_result, collection);
    }
}

现在很明显,您可以取消对explore1(调用heavy_computation)的调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-15
    • 2019-04-28
    • 1970-01-01
    • 2016-06-23
    • 1970-01-01
    • 2022-06-25
    • 1970-01-01
    • 2011-04-06
    相关资源
    最近更新 更多