如何理解这个递归函数?

时间:2017-06-01 03:55:28

标签: c++ recursion

  

找出给定整数x可以表示为唯一自然数的n次幂之和的方式的数量。

     

例如:如果x = 10,则n = 2。唯一可能的情况是:3 ^ 2 + 1 ^ 2

这是我在某处读到的解决方案:

int solve(int x, const vector<int> &powers, int index) {
    if(index == 0) {
        return (x == 1) ? 1 : 0;
    }
    // else
    if(x == powers[index])
        return 1 + solve(x, powers, index - 1);
    // else
    int res = 0;
    res += solve(x - powers[index], powers, index - 1);
    res += solve(x, powers, index - 1);
    return res;
}


int main() {
    int x, n;
    cin >> x >> n;

    int pow = 1;
    vector<int> powers;
    for(int a = 2; pow <= x; a++) {
        powers.push_back(pow);
        pow = power(a, n);
    }

    cout << solve(x, powers, powers.size() - 1) << endl;        
    return 0;
}

在这里,我无法实际想象它。如何递归思考?请解释一下解决功能?

2 个答案:

答案 0 :(得分:4)

为了更好地理解递归,我建议你运行以下代码,它为函数调用(入口)提供可视化并返回:

#define TRACKING

#include <cmath>
#include <vector>
#include <iostream>

#ifdef TRACKING
#include <iomanip>
#include <algorithm>
const unsigned int maxOutOffset = 60U;
#endif

using namespace std;


int solve(int x, const vector<int> &powers, int index)
{
#ifdef TRACKING
    int outputOffset = min(powers.size() - index, maxOutOffset);
    cout << setw(outputOffset) << ">" << " entrance with index = " << index << " and x = " << x << endl;
#endif
    int res = 0;
    if (index == 0) {
        res = (x == 1) ? 1 : 0;
    }
    else if (x == powers[index])
    {
        res = 1 + solve(x, powers, index - 1);
    }
    else
    {
        res += solve(x - powers[index], powers, index - 1);
        res += solve(x, powers, index - 1);
    }
#ifdef TRACKING
    cout << setw(outputOffset) << "<" << " return with index = " << index << " and res = " << res << endl;
#endif
    return res;
}


int main() {
    int x, n;
    cout << "x ? : ";
    cin >> x;
    cout << "n ? : ";
    cin >> n;

    int power = 1;
    vector<int> powers;
#ifdef TRACKING
    cout << "Powers privied to solve method:" << endl;
#endif
    for (int a = 2; power <= x; a++) {
#ifdef TRACKING
        cout << power << ", ";
#endif
        powers.push_back(power);
        power = pow(a, n);
    }
#ifdef TRACKING
    cout << endl << "Tracking the recursive solution:" << endl;
#endif
    cout << "Results is "<< solve(x, powers, powers.size() - 1) << endl;
    return 0;
}

使用x = 10n = 2运行的结果如下:

enter image description here

我真的希望,提议的跟踪将帮助您理解递归并使您的解决方案更好(对应于任务)。

<强>更新

如果删除第一行(#define TRACKING),则输出如下:

enter image description here

更新2

递归是一种借助同一功能的一些功能。例如:n! = n*(n-1)!

如果我们考虑你的特定情况(在使用求和n的权力数组中查找/计算x个数字的任务 任务可以表示为:

  

从数组中的最大数字开始,尝试使n个项目的总和为x,

     

其中添加下一个表示的数字   number_count_i(x)= 1 + number_count_i-1(x-item [i])

     

我们应该在x等于item [i]

时停止

也许,我并不是非常严格,所以试着以这样的方式思考并在纸上画画(这确实有帮助)。

答案 1 :(得分:0)

  

如何递归思考?

在创建MCVE的粗糙哑剧中,让我首先通过“隐藏”大多数非递归问题来“最小化”函数:

int solve(... int index) 
{
    if(index == 0)   // termination clause
        return (x == 1) ? 1 : 0; // result

    // else
    if(x == powers[index])
        return 1 + solve(... index - 1);  // recurse

    // else
    {
      int res = 0;
      res += solve(... index - 1);  // recurse
      res += solve(... index - 1);  // recurse
      return res;
    }
}

注意在solve()的3次调用中,参数(在本例中为index)是如何递减的。这一行动最终会导致终止案件。

记住这种努力如何进入终止子句,现在想象一下,作为这个思想实验的一部分,每次调用“solve(index)”都会转换为一个唯一命名的函数:

solveN(), ... solve3(), solve2(), solve1(), solve0(), 

其中solveK()映射到solve(K)。这种形式是一种“正常”函数调用,应该是可识别的,并且仅仅是原始递归形式的“伪装”。

(我们需要知道有多少电话?这是一个思想实验,所以,不。)

现在,让我们检查索引3的简化函数调用堆栈,直到0。要点是

solve3(...) { return (solve2(...)); }

   solve2(...) { return (solve1(...)); }

      solve1(...) { return (solve0(...)); }

         solve0(...) { returns 0 or 1 }

foo()可能调用bar()可能会调用bric()调用brac(),这可能是正常的并且可以识别。每个函数都会完成整个工作的某些部分,每个函数都会返回,我们称之为“内部”结果,当每个函数返回给它的调用者时,它会被(可能)细化。

调用堆栈中的缩进暗示了自动变量空间的使用(在大多数hw体系结构中称为堆栈)......

摘要 - 递归调用完全就像任何函数调用一样。 N深度递归必须返回N次,并且每次返回的位置与任何正常函数返回位置完全相同,即在调用之后。

每次递归都需要至少一个终止子句。让代码不终止是错误的(即使它有一个终止子句)。

希望这有帮助。