这是我解决“具有最小处理能力的斐波那契级数的第n个项”的想法-
int fibo(int n, int a, int b){
return (n>0) ? fibo(n-1, b, a+b) : a;
}
main(){
printf("5th term of fibo is %d", fibo(5 - 1, 0, 1));
}
要打印所有术语,直到第n个术语,
int fibo(int n, int a, int b){
printf("%d ", a);
return (n>0)? fibo(n-1, b, a+b): a;
}
我向我的大学教授展示了此代码,根据她的说法,这是解决斐波那契问题的错误方法,因为它没有抽象该方法。我应该将函数称为fibo(n)而不是fibo(n,0,1)。这对我来说不是一个令人满意的答案,因此我想到向SOF的专家咨询。
与解决斐波那契问题的传统方法相比,它具有自己的优势。我们采用两次并行递归来获得斐波那契第n项(fibo(n-1)+ fibo(n-2))的技术可能很慢,无法给出该系列的第100个项,而即使在最坏的情况下,我的技术也会更快场景。
要抽象化它,我可以使用默认参数,但C语言不是这种情况。尽管我可以使用-
int fibo(int n){return fiboN(n - 1, 0, 1);}
int fiboN(int n, int a, int b){return (n>0)? fiboN(n-1, b, a+b) : a;}
但是抽象整个想法就足够了吗?我应该如何说服其他人该方法没有错(尽管有点含糊)?
(我知道,这不是我应该对SOF提出的问题,但我只是想从这里征求专家的意见。)
答案 0 :(得分:4)
了解到递归的基本情况应该是a
而不是0
,因此在我看来,这是一个很好的解决方案(尽管不是最佳选择)。该函数的递归是尾递归,因此,好的编译器将能够避免堆栈增长,从而使函数O(1)停滞和O(n)成为时间(忽略数字大小的快速增长)。>
您的教授是正确的,即呼叫者不必处理正确的初始化。因此,您应该提供一个外部包装程序,从而避免填写这些值。
int fibo(int n, int a, int b) {
return n > 0 ? fibo(b, a + b) : a;
}
int fib(int n) { return fibo(n, 0, 1); }
但是,如果调用者实际上想要更改初始值,则提供和记录更通用的接口也可能很有用。
顺便说一句,基于递归,有一种更快的计算技术
fib(a + b - 1) = f(a)f(b) + f(a - 1)f(b - 1)
用b
代替b + 1
会产生:
fib(a + b) = f(a)f(b + 1) + f(a - 1)f(b)
这些公式可以让我们计算:
fib(2n - 1) = fib(n + n - 1)
= fib(n)² + fib(n - 1)²
fib(2n) = fib(n + n)
= fib(n)fib(n + 1) + fib(n - 1)fib(n)
= fib(n)² + 2fib(n)fib(n - 1)
这允许以O(log n)步骤执行计算,每个步骤产生两个连续的值。
答案 1 :(得分:2)
使用您的方法,您的结果将是 0 。您只需递归,直到 n = 0 ,然后返回 0 。但是,您还必须检查 n == 1 的时间,并且应该返回 1 ;另外,您还具有 a 和 b 值,而您对它们不执行任何操作。
我建议您看一下下面的递归函数,也许它将有助于修复您的递归函数:
int fibo(int n){
if(n < 2){
return n;
}
else
{
return (fibo(n-1) + fibo(n-2));
}
}
这是研究递归的经典问题。
EDIT1:根据@Ely的建议,波纹管是一种具有记忆技术的优化递归。计算列表中的一个值后,将不会像第一个示例中那样再次对其进行重新计算,但是将其存储在数组中,并在需要时从该数组中获取:
const int MAX_FIB_NUMBER = 10;
int storeCalculatedValues[MAX_FIB_NUMBER] = {0};
int fibo(int n){
if(storeCalculatedValues[n] > 0)
{
return storeCalculatedValues[n];
}
if(n < 2){
storeCalculatedValues[n] = n;
}
else
{
storeCalculatedValues[n] = (fibo(n-1) + fibo(n-2));
}
return storeCalculatedValues[n];
}
答案 2 :(得分:1)
使用递归,并以最低处理能力为目标,一种解决fibonacci()
的方法是让每个呼叫返回2个值。也许一个通过返回值,另一个通过int *
参数。
递归的通常想法是让顶层函数一次性执行准备工作并检查参数,然后以精简的方式编写本地帮助函数。
以下内容遵循OP的int fibo(int n)
和助手int fiboN(int n, additional parameters)
递归深度为O(n),内存使用量也为O(n)。
static int fib1h(int n, int *previous) {
if (n < 2) {
*previous = n-1;
return n;
}
int t;
int sum = fib1h(n-1, &t);
*previous = sum;
return sum + t;
}
int fibo1(int n) {
assert(n >= 0); // Handle negatives in some fashion
int t;
return fib1h(n, &t);
}
答案 3 :(得分:0)
求解“具有最小处理能力的斐波那契级数的第n个项”
我可能不需要向您解释斐波那契数的递归关系。尽管您的教授给了您很好的提示。
提取细节。她是对的。如果您想要第n个斐波那契数,只需告诉程序仅Fibonacci(n)
因为您的目标是提高处理能力,您的教授的提示也适用于称为memoization的技术,这基本上意味着如果您一次计算了第n个斐波那契数,则只需重用结果;无需重做计算。在本文中,您可以找到阶乘数的示例。
为此,您可能需要考虑一个存储第n个斐波那契数的数据结构;如果该内存中已有斐波那契数,则只需检索它,否则将计算出的斐波那契数存储在其中。
顺便说一句,从理论上讲并没有帮助,但很有趣:第n个斐波那契数也有一个closed form expression。
这对我来说不是一个令人满意的答案,所以我想到了问 SOF专家。
“呃,您不认为您的教授是专家吗?”是我的第一个念头。
答案 4 :(得分:0)
作为旁注,您几乎可以进行fibonacci problem
而无需递归,这使其成为我所知最快的方法。该代码虽然在Java中:
public int fibFor() {
int sum = 0;
int left = 0;
int right = 1;
for (int i = 2; i <= n; i++) {
sum = left + right;
left = right;
right = sum;
}
return sum;
}
答案 5 :(得分:0)
尽管@rici的回答大部分令人满意,但是我只想分享我学到的解决该问题的知识。因此,这就是我对使用递归查找斐波那契的理解-
fibo(n) { return (n < 2) n : fibo(n-1) + fibo(n-2);}
在时间和空间要求方面都效率低下。这不必要地构建堆栈。它需要O(n)个堆栈空间和O(r n )时间,其中r =(√5+1)/ 2。fib(2n) = fib(n)² + 2fib(n)fib(n - 1)
中,正如他建议将时间复杂度降低到O(log n)一样,我想堆栈的增长仍然是O(n)。因此,我的结论是,如果我进行了适当的研究,则无法使用递归计算同时降低时间复杂度和空间需求。为了达到这两个目的,替代方案可以使用迭代Matrix exponentiation or fast doubling。