12 | 数学归纳法:搞定循环与递归的钥匙

你好,我是胡光,今天我们正式开始“编码能力训练篇”的学习。

这里给你一个建议,在刚刚完成了语言基础篇的学习后,我希望你用心地体验“螺旋式上升”的学习过程。就是前面的基础篇虽然学完了,可并不是意味着,不需要再学习更多的语言相关的东西了,你可以做如下两件事情:

  1. 对于语言基础,你可以选择学习第二遍,当你站在第一遍的基础上,再回头看的时候,肯定会对之前的知识有更深的理解;
  2. 选择在其他参考资料中,继续学习语言中更多的知识点。你会发现,某些之前自己认为晦涩难懂的东西,可以自学搞明白了,这就是我提到的“螺旋式上升”的学习方法。

在接下来的“编码能力训练篇”里,我将着重给你讲解一些编程中的重要技巧。今天呢,我们就从理解循环与递归的编码技巧开始吧!

今日任务

循环结构,你已经不陌生了,如下代码所示,是一个单层循环的程序,依次地输出从 1 到 n 的每一个数字,每个数字占一行:

#include <stdio.h>

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        printf("%d\n", i);
    }
    return 0;
}

当我们输入 4 的时候,程序的输出结果如下所示:

1
2
3
4

上面这个是单层循环的情况。下面这个例子,是一个双层循环的例子,每层循环都从 1 循环到 n,循环内部每次输出两个循环遍历的值:

#include <stdio.h>

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            printf("%d %d\n", i, j);
        }
    }
    return 0;
}

当我们输入 3 的时候,程序的输出结果如下所示:

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3

看了上面单层循环和双层循环的例子以后,如果让你改写成类似的三层循环的程序,想必这个你一定会做,无非就是在两层循环的内部,多加一层循环,然后 printf 输出的时候,输出的是三个变量的值即可。如果你可以自己理解到这个程序,那么你就可以理解今天这个任务。

今天这个任务,和上面的例子类似,但它不是实现一层循环的程序,也不是实现三层循环的程序,而是实现一个 k 层循环的程序。什么意思呢?就是 k 是一个读入参数,之后再读入一个参数 n,含义和上述程序中的 n 一致,而这个程序的输出结果,与上述例子中的输出结果类似,只不过每行输出 k 个数字。

简单来说,你要实现的是一个可变循环层数的程序。这下你清楚今天的任务了吧?那么我们正式开始学习吧。

必知必会,查缺补漏

理解了上面这个任务要做什么了,你可能还会发懵:为什么循环层数是可变的,代码结构不是确定性的么?别着急,今天我们将学习一个重要的编程技巧,那就是递归。

这里我要提醒一下,递归是一种编程技巧。你可能会在某些资料中,看到递归算法这种说法,其实这种说法是不合适的,因为明显的事实是,能够用循环实现的算法,都可以用递归这种编程技巧实现。如果递归算作算法,那你听过循环算法一说么?所以,用一个编程技巧,给一类算法命名,实际是不合适的。

1. 温故知新:数学归纳法

你知道么,计算机的本质,是一个用来计算的工具,它最开始就是帮助我们完成一些现实世界里面的计算任务,并且完成的又快又好。那么现实世界的问题,是如何转换成可以在计算机中计算的任务呢?这个转换的过程中,都有哪些必不可少的东西呢?请看下图:

在这幅图中,我们把转换过程分成四个部分:“现实世界”“数学”“算法”和“计算机”。这四个部分形成了一个路线,也就是从现实世界中的实际问题,到计算机中的可计算任务的过程。

我稍微来详细解释一下这幅图所表达的含义。首先我们来想想,如果没有数学,现实生活中我们会遇到什么困难?我会毫不夸张地告诉你,可能会面临生存危机。试想一下,因为没有数学,我们不会计算每日食物的消耗,无法合理分配资源,导致食物匮乏,引发生存危机。这也是为什么人类最早的文字记录,或者说是信息传递,用的是结绳记事,以“算术”的形式来解决现实世界问题。可以说,现实世界中的问题,本质是可以计算的,也就是说实际问题都可以做数学建模。

然后,我们说说算法。算法是将数学问题,转换到计算机中的计算任务的桥梁。因为计算机是依靠指令序列来执行的,而不同的指令序列代表了不同的效率,不同的效率在很多时候就意味着可行或者不可行。试想一个数学抽象出来的公式,需要计算机运算1000年才能得出结果,你认为这种任务可以放到计算机上面做么?答案显然是否定的。算法就是使得计算任务变得更高效,更可行。

至此,你就对我所说的内容,有个大致的体会了:计算机的核心是算法,算法的核心是数学。接下来呢,我们就需要介绍一种,可以指导我们进行程序设计的数学方法:数学归纳法。

高中的时候,我们就接触过数学归纳法,你可能已经对这个概念了然于胸,不过我们还是来回顾一下数学归纳法证明过程中重要的三步骤。

其实数学归纳法的三个步骤,总结起来就是,有一个已知正确的初始状态,然后证明如果前一个状态成立,那么后一个状态也成立(这一步主要在做过程正确性的证明),最后就是得出结论,在这个初识状态和转移过程的正确保证下,所有问题中的状态都成立。

举个例子,便于你更好地理解。假设我们要利用数学归纳法来证明:如果我推倒了第一块多米诺骨牌,那么所有的多米诺骨牌都会倒下。那么放到这三个步骤里,就是:

  • 第一步,验证边界条件,第一块多米诺骨牌倒下了。
  • 第二步,就是假设,第 n 块倒下了,根据多米诺骨牌的结构性质,那么如果存在 n + 1 块,第 n + 1 块也一定会倒下。
  • 第三步,得出结论,只要第一块倒了,所有的多米诺骨牌都会倒下。

注意,上面说的这个是广义层面数学归纳法,这个过程对于循环过程的正确性证明,是非常有效的。

想一想,进入循环之前的程序中关键变量的值,就是上面所说的第一步中的 k0;而每一次的循环,其实就是第二步中所要证明的那个上一个状态到下一个状态的过程。如果这两者都正确,我们就能很确信地知道,我们的整个循环过程就是正确的。

关于上面说的数学归纳法和循环程序之间的这一点联系,在日后的学习中,我还会详细地去举例说明,尤其是到了后续,我们学到了递推算法和动态规划算法的时候,会尤为明显。所以你要有足够的耐心和信心,咱们一起把这些问题搞懂。

2. 深入浅出:理解递归函数

放在编程的语境中,什么是递归呢?我这里先强调一句:递归是一种编程技巧。

你学完了函数以后,已经可以熟练地掌握在一个函数中,调用另外一个函数的方法了。可你有没有想过,如果在某个函数内部,调用自己同名函数过程,会发生什么?其实,和普通的函数调用过程一样,在具体执行过程中,只有等内部调用的函数执行完后,本层函数才会继续执行。

递归是一个过程,这个过程的每一步都类似,只是面对的问题规模不同。

下面我来举个例子:假如今年我上小学5年级,我现在想知道1~5年级的年级主任名字,但我现在只知道5年级的年级主任的名字,我可能会问一个4年级的学弟,希望他能告诉我1~4年级主任的姓名。

我这个学弟呢,也只知道他们年级主任的名字,那么我这个学弟就会问3年级学弟,问他3年级及以下的年级主任都有谁,依次类推,最后到了1年级的小学弟。

1年级的小学弟,就会告诉2年级的学长自己年级主任的名字,2年级的学长拿到1年级的年级主任的名字以后,会把2年级年级主任的名字填上去,然后再交给3年级的他学长……这样最终到我手里的就会是1~4年级的年级主任的所有名字,再加上我自己知道的5年级的年级主任姓名,这样,我就知道了全部信息。整个过程,如下图所示:

在这个过程中,每个人问学弟的过程,就是我们所谓的“递”,而拿到学弟给的结果名单以后,再加上自己知道的结果反馈给自己学长的这个过程,就是“归”,整个过程就是我们所谓的“递归”。“递归”的过程,每一步的过程类似,可是问题规模不同。

接下来,我来举一个编程中的具体递归例子,看如下代码:

#include <stdio.h>

int f(int n) {
    if (n == 1) return 1;
    return f(n - 1) * n;
}

int main() {
    int n;
    scanf("%d", &n);
    printf("%d\n", f(n));
    return 0;
}

这段代码中,f 函数的作用,是计算 n 的阶乘的值,也就是从 1 乘到 n 的结果。在 f 函数内部,首先是一个边界条件,就是当 n == 1 的时候,直接返回 1 的阶乘的结果。否则,n 的阶乘的结果,应该等于 n - 1 阶乘的结果再乘上 n ,就得到了 n 的阶乘。在得到 n - 1 阶乘结果的过程中,我们调用的不是别的函数,还是 f 函数本身,只不过传入的参数范围,是一个比 n 更小的范围 n - 1。

关于这个 f 函数,类比于上面年级主任的那个例子,f(n) 就是我整理的信息,f(n - 1)就是比我要小 1 个年级的学弟所整理得到的信息,而 n == 1 的边界条件判断,就是我那个最小的 1 年级的学弟。最后 f(n - 1) * n 当中的 * n 这个过程,就相当于每个人拿到了学弟整理的信息以后,再加上自己知道的信息,最后递交给自己的学长。

为什么这么做,能保证每个人所得到的信息都是正确的呢?在证明这个过程的时候,我们就需要用到前面提到的数学归纳法了。首先,我们知道 1 年级的学弟肯定能给出正确的信息,这就是数学归纳法中的边界条件。然后我们假设,如果上一个学弟,给出的信息是正确的,那么我所整理出来的信息,就一定是正确的,这就是数学归纳法中的证明过程的正确性。最终,我们就可以得到结论,在这个过程中,所有人获得的信息都是正确的,包括我自己。

其实,到了这里,我们也就得到了递归程序设计中的重要的两部分:边界条件处理过程

  • 所谓边界条件,就是当递归函数中的参数等于多少的时候,可以直接返回的条件。
  • 处理过程呢,就是设计程序过程,处理递归调用的返回结果,根据递归调用的返回结果,得到本函数的结果。

这两部分,分别对应了数学归纳法中的两步,step1和step2。当这两步都可以保证正确,所涉及的递归函数程序,也绝对是正确的。

一起动手,搞事情

今天的思考题呢,是关于一段递归程序的:

#include <stdio.h>

int fib(int n) {
    if (n == 1 || n == 2) return 1;
    return fib(n - 1) + fib(n - 2);  
}

int main() {
    int n;
    scanf("%d", &n);
    printf("%d\n", fib(n));
    return 0;
}

上面这段程序中,fib 函数是求菲波那契数列第 n 项值的函数。菲波那契数列的定义如下:

根据如上内容,你需要完成两个小的思考题:

  1. 请将上述菲波那契数列求解的程序从递归程序,改成循环程序。
  2. 请将上述递归程序的代码和数学归纳法中的步骤做一一对应,留在留言区中。

完成不定层数的循环程序

准备完了基础知识以后,让我们回到今天的任务,完成一个可变循环层数的程序。我们可以一开始假设,有一个函数,是实现 5 层循环打印的程序,那么它会循环 n 次,每次调用一个实现 4 层循环打印的程序。

依照这个大体的思路,我们就可以写出如下代码框架:

int print_loop(int k, int n) {
    if (k == 0) {
        // 打印一行
    }
    for (int i = 1; i <= n; i++) {
        print_loop(k - 1, n);
    }
    return;
}

在这个代码框架中,我们先来看递归的过程,print_loop(k, n)代表 k 层循环的程序,然后循环 n 次,每次调用一个 k - 1 层循环的程序。而递归的边界条件就是当 k == 0 的时候,就是所谓的 0 层循环,也就是程序打印一行具体内容的地方,可打印的这行内容究竟是什么呢?

你会发现,要打印的这行内容,与每层循环遍历到的数字有关系,那么我们就需要记录每层循环遍历到的数字。这个信息,我们可以记录在一个数组中,数组中存储的,就是当前要打印这行的每一个数字。基于上述代码框架,我们就可以得到下面这个更完善的代码:

int arr[100];
void print_loop(int k, int n, int total_k) {
    if (k == 0) {
        for (int i = total_k; i >= 1; i--) {
            if (i != total_k) printf(" ");
            printf("%d", arr[i]);
        }
        printf("\n");
        return ;
    }
    for (int i = 1; i <= n; i++) {
        arr[k] = i;
        print_loop(k - 1, n, total_k);
    }
    return ;
}

正如你看到的,我们把每一层循环的值,放到了一个 arr 数组中,第 k 层循环变量的值,存储到 arr[k] 的位置。而在上述代码中,多了一个递归参数,就是 total_k,代表了一共有多少层循环,这个参数是为了方便我们最后确定循环输出的上界。至此,我们就完成了今天的任务。

课程小结

今天的重点,一个关于数学归纳法,一个关于递归,需要你记住如下两点:

  1. 数学归纳法中重要的两部分,一是要边界条件成立,二是证明转移过程成立。
  2. 程序设计最重要的是正确性,递归函数的正确性可以利用数学归纳法来保证。

关于数学归纳法和递归函数的设计,还需要你在日后不断的加以练习。注意总结两者的联系,能够使得你在接下来的学习中事半功倍。

好了,今天就讲到这里,我是胡光,我们下期见。

精选留言

  • 学写代码的猪

    2020-02-13 00:39:55

    int f1, f2 ;
    f1 = f2 = 1 ;
    for(int i = 3; i <n; i++){
    f2 = f1 + f2;
    f1 = f2 - f1;
    }
    作者回复

    完美!

    2020-02-13 19:52:04

  • 武汉李先生

    2020-09-30 21:25:54

    胡船长,我理解递归有点困难,就举了个简单的例子:k=3,n=3。代入手推了一下,第k层循环应该是最左边一列的数字,第1层循环应该是最右边一列的数字。我的理解递归就是,从第1层开始循环1~n,此时2~k层都是初始值1,当第一层结束循环后,第2层进行循环的第二轮,就需要第1层继续循环1~n,然后第2层开始第三轮,第一层照样循环一遍,直到第2层完成最后一轮的循环,递归到第3层,第3层进行第一轮循环,就需要第2层重新循环一遍,第2层循环每一遍,对应需要第1层完整循环。
    我推算到这里就想起来现在很火的深度学习里的递归神经网络,可以把这个任务的结果从左到右看成神经网络的每一层。
  • look for

    2022-10-12 17:09:49

    今天的任务,递归程序,如果想它代码的执行过程,好难理解啊!
  • 乐毅

    2020-10-11 11:12:54

    个人觉得理解递归还需要一些基础比如每次函数调用时产生的栈桢
  • wu526

    2020-05-20 15:45:59

    老师您好,文章中最后实现的print_loop函数我不能很好的题目的关系对应起来,可以举个例子吗?谢谢老师。
    作者回复

    首先,理解递归程序,要理解递归子问题的概念,以及递归问题和子问题之间的同构性,证明递归程序的正确性,是需要利用数学归纳法的。打印五层循环的程序,需要依赖打印四层循环的程序,从4层到5层,这就是数学归纳法中的 k(i) --> k(i+1) 的过程。递归的边界条件中,实现了打印1层循环的程序,这就是数学归纳法的 k(0) 成立,结合在一起,我们就能很轻松的知道,我们所实现的递归程序是否正确。这种思维过程,还需要你在日后的学习中,不断的加强锻炼,以便于你日后能理解和设计出更复杂的递归程序。

    2020-06-15 17:45:36

  • Jinlee

    2020-02-13 11:19:15

    斐波那契循环部分:

    int fib(int n) {
    int f1 = 0, f2 = 1;
    int i, res;
    for (i = 1; i <= n; i++){
    res = f2;
    f2 = f1 + f2;
    f1 = res;
    }
    return res;
    }

    int fib(int n) {
    int f1 = 0, f2 = 1;
    int i;
    for (i = 1; i <= n; i++){
    f1, f2 = f2, f1 + f2;
    }
    return f1;
    }
    一开始写的代码是②,但是发现输出结果永远是0。这就说明经过for循环之后f1的值并没有改变,这是为什么呢老师?
    作者回复

    你应该是学过 python 吧,C 是不支持这种 f1,f2 = f2, f1 + f2 这种语法的。这段代码,在 C 语言中,会被认为是一个逗号表达式,其中包括三个独立的语句,第一个是 f1、第二个是 f2 = f2,第三个是 f1 + f2。

    2020-02-13 19:51:21

  • look for

    2022-10-12 17:48:34

    思考题

    int fib(int n)
    {
    int f1 = 1;
    int f2 = 1;
    for (int i = 3; i <= n; i++)
    {
    f2 = f1 + f2;
    f1 = f2 - f1;
    }
    return f2;
    }
  • 武汉李先生

    2020-09-30 21:23:03

    胡船长,我理解递归有点困难,就举了个简单的例子:k=3,n=3。代入手推了一下,第k层循环应该是最左边一列的数字,第1层循环应该是最右边一列的数字。我的理解递归就是,从第1层开始循环1~n,此时2~k层都是初始值1,当第一层结束循环后,第2层进行循环的第二轮,就需要第1层继续循环1~n,然后第2层开始第三轮,第一层照样循环一遍,直到第2层完成最后一轮的循环,递归到第3层,第3层进行第一轮循环,就需要第2层重新循环一遍,第2层循环每一遍,对应需要第1层完整循环。
    我推算到这里就想起来现在很火的深度学习里的递归神经网络,可以把这个任务的结果从左到右看成神经网络的每一层。
  • 罗耀龙@坐忘

    2020-06-20 00:16:47

    茶艺师学编程

    通过动手把递推程序回归至循环程序,体会到递推以及数学归纳法的便利所在——能用简单的几句话(代码)就把事情搞定了。

    顺带提一下菲波那契数列,这玩意真的很有趣:
    1、明明数列的每一项都是自然数,然而它的通项公式却用无理数来表达;

    2、后一项比前一项的比值的极限,就是黄金比例:(√5-1)/2

    思考题1:
    1、验证边界条件K0成立——> if ( n == 1 || n == 2) return 1
    2、假设Ki成立,证明Ki+1也成立——>return fib(n - 1) + fib(n - 2)
    3、所有Kn都成立

    思考题2:
    /*思考题 数组方法 */
    #include <stdio.h>
    #include <stdlib.h>
    int main(){
    printf("此方法产生的斐波那契数列长度等于与数组arr设定的容量大小。\n\n") ;
    int arr[10] = {1, 1};//设定数组,且第一和第二为“1”
    int i = 0;//在此环境里,指针移动变量i需要设定在for循环外
    for(i = 2; i < 10; i++){//该循环就是按照斐波那契数列的规则填满该数组剩下的空位
    arr[i] = arr[i - 1] + arr[i - 2];
    }
    for (i = 0; i < 10; i++){//该循环是,把指针回到一开始,然后按顺序把数组的数字输出
    printf(" %d", arr[i]);
    }
    printf("\n\n");
    system("pause");
    return 0;
    }
    /*思考题 简单方法 */
    #include <stdio.h>
    #include <stdlib.h>
    int main(){
    int a, b, n;
    printf("请问你要生成多长的裴波那契数列?\n\n");
    scanf("%d", &n);
    a = 1;
    b = 1;
    for(int i = 0;i < n; i++){
    printf(" %d %d", a, b);
    a = a + b;
    b = b + a;
    }
    printf("\n\n\n");
    system("pause");
    return 0;
    }

    附带:课文例子的完整可用版
    /*课文例子的完全体*/
    #include <stdio.h>
    int arr[100];
    void print_loop(int k, int n, int total_k){
    if (k == 0){
    for( int i = total_k; i >= 1; i--){
    if(i != total_k)printf(" ");
    printf("%d", arr[i]);
    }
    printf("\n");
    return ;
    }
    for (int i = 1; i <= n; i++){
    arr[k] =i;
    print_loop(k - 1, n, total_k);
    }
    return;
    }

    作者回复

    d(^_^o),进步神速啊!

    2020-07-11 01:00:35

  • Noya

    2020-05-18 01:23:43

    #include <cstdio>

    typedef long long ll;

    int main()
    {
    int n;
    scanf("%d", &n);
    ll f1 = 1, f2 = 1;
    if (n == 1 || n == 2)
    puts("1");
    else
    {
    for (int i = 3; i <= n; i++)
    {
    f2 = f1 + f2;
    f1 = f2 - f1;
    }
    printf("%lld\n", f2);
    }
    return 0;
    }
    作者回复

    很不错!能用两个变量搞定 fib 求和的过程!赞!

    2020-06-15 17:53:06

  • 1043

    2020-04-09 21:29:33

    在程序世界的数学归纳法只要合逻辑就可以应用在算法中实现,虽然也要经过逻辑演绎、三段论推断但是不完全像现实世界那样要经过把引理证明到没有逻辑瑕疵的定理才能用。而是直接可以把逻辑归纳用于计算机算法程序在有限步骤下的有理数计算通过即可。只是如果更符合数学定理和逻辑这个算法就更完美而已。
  • 王楷程

    2020-03-29 17:32:34


    int arr[100];
    int print_loop(int k, int n, int total_k) {
    if (k == 0) {
    for (int i = total_k; i >= 1; i--) {
    if (i != total_k) printf(" ");
    printf("%d", arr[i]);
    }
    printf("\n");
    }
    for (int i = 1; i <= n; i++) {
    arr[k] = i;
    print_loop(k - 1, n, total_k);
    }
    return;
    }
    这段代码的return 必须有返回值的吧,而且return 要写在前面,否则,当k == 0时,就会发生越界访问
    作者回复

    int arr[100];
    void print_loop(int k, int n, int total_k) {
    if (k == 0) {
    for (int i = total_k; i >= 1; i--) {
    if (i != total_k) printf(" ");
    printf("%d", arr[i]);
    }
    printf("\n");
    return ;
    }
    for (int i = 1; i <= n; i++) {
    arr[k] = i;
    print_loop(k - 1, n, total_k);
    }
    return ;
    }

    2020-03-30 00:33:26

  • 潮汐

    2020-03-12 10:04:44

    作者回复: 你这个程序编译的时候,没有报错??你试试把 aboutRecursion 以及下面那个 f 函数放到主函数前面去。
    --------------
    回复老师:我这个编译是没问题。我把那两个函数搬到main前面也是一样的现象。
    作者回复

    哦哦~~~我看到了,你主函数里面,根本就不是在调用 aboutRecursion 函数啊~~~~你是写了一行 aboutRecursion 函数的声明。

    2020-03-12 12:58:31

  • 潮汐

    2020-03-09 16:03:17

    老师,有个问题请教:
    int main() {
    int aboutRecursion();
    }
    int aboutRecursion() {
    int n;
    scanf("%d", &n);
    printf("%d\n", f(n));
    return 0;
    }
    int f(int n) {
    if (n == 1) return 1;
    return f(n-1) * n;
    }

    我这套代码编译之后运行,并不能按正常的逻辑输入一个n,程序就已经执行完。我尝试将aboutRecursion里的代码主体全部搬到main里,却能正常。请问这是怎么回事呢?
    作者回复

    你这个程序编译的时候,没有报错??你试试把 aboutRecursion 以及下面那个 f 函数放到主函数前面去。

    2020-03-11 22:21:37

  • 潮汐

    2020-03-09 15:59:56

    斐波那契数列的思考题
    int fib(n) {
    // 递归写法
    /*
    if (n == 1 || n == 2) return 1; // 终止条件 -- 数学归纳法step1
    return fib(n-1) + fib(n-2); // 处理过程 -- 数学归纳法step2
    */

    // 循环写法
    int f1 = 1, f2 = 1, f3;
    for (int i = 3; i <= n; i++) {
    f3 = f1 + f2;
    f1 = f2;
    f2 = f3;
    }
    return f3;
    }

    看了评论,原来可以优化只用两个变量
    作者回复

    yes!

    2020-03-11 22:22:01

  • 宋不肥

    2020-02-18 00:25:48

    老师,能不能把k==0的时候每次输出的一行数认为是k==1的循环的一个元素,把k==1认为是边界条件呀,这样理解可以吗
    作者回复

    可以的。

    2020-02-18 17:42:34

  • 宋不肥

    2020-02-17 18:09:40

    感觉理解示例代码得关键在于理解“递归的边界条件就是当 k == 0 的时候,就是所谓的 0 层循环,也就是程序打印一行具体内容的地方”这句话,看了半个小时才反应过来。。。
    作者回复

    ⁄(⁄ ⁄ ⁄ω⁄ ⁄ ⁄)⁄我想想换一种表述。

    2020-02-18 00:03:33

  • 2020-02-12 18:06:44

    斐波那契循环方法
    int fib(n) {
    int first = 1;
    int second = 1;
    for(int i = 3; i < n; i++) {
    second = first + second;
    first = second - first;
    }
    return (first + second);
    }
    作者回复

    我猜,你这段代码,绝对没有经过测试。有一点儿小瑕疵,但是逻辑是正确的。

    2020-02-13 19:52:45

  • 徐洲更

    2020-02-12 15:39:20

    不知道这样子算不算只用了f1和f2
    int fib(int n){
    if ( n < 2) return 1;
    int f1=1;
    int f2=1;
    int tmp;
    for (int i = 1; i < n; i++){
    tmp = f2;
    f2 = f1 + f2;
    f1 = tmp;
    }
    return f2;
    }

    1. 当x=0,或 x=1时,f(x) = 1
    2. 当x=n时, f(x) = f(x-1) + f(x-2)
    作者回复

    不算哦~~~~其中有一个 tmp 变量是额外使用的,所以,这段程序还是可以优化的。

    2020-02-13 19:54:41

  • Cache

    2020-02-11 18:12:45

    斐波那契改为循环结构:
    主要代码:
    int f1,f2;
    f1 = f2 = 1;

    for (int i = 3; i <= n; i++) {
    f3 = f1 + f2;
    f1 = f2;
    f2 = f3;
    }
    作者回复

    不错!试试能不能只使用f1和 f2

    2020-02-11 19:19:54