zoukankan      html  css  js  c++  java
  • C语言--递归程序的设计

    一、递归程序的定义

    程序调用自身的编程技巧叫做递归

    递归程序的组成部分

    1.语义信息

    2.边界条件

    3.针对于问题的处理过程和递归过程 (推导出一个递推式子)

    4.结果返回

    注意:函数的结果返回有两种方式,分别为1.return返回;2.传出参数返回(通过指针变量去实现)

    例子:编写一个n的阶乘的程序。

     1 /*************************************************************************
     2     > File Name: 4.c
     3     > Author: yudongqun
     4     > Mail: qq2841015@163.com
     5     > Created Time: Sat 07 Nov 2020 08:27:04 PM CST
     6  ************************************************************************/
     7 #include <stdio.h>
     8 
     9 int factor(int n) {//(1)语义信息
    10     if (!n || n == 1) {
    11         return 1;//(2)边界条件
    12     }
    13     return n * factor(n - 1);//(3)推导出一个递推式子,并(4)结果返回
    14 } 
    15 
    16 int main(void) {
    17     int n;
    18     while (scanf("%d", &n)) {
    19         printf("%d! = %d
    ", n, factor(n));
    20     }
    21     return 0;
    22 }

    编译并运行。

    ydqun@VM-0-9-ubuntu chapter4 % gcc 4.c                                                    [130]
    ydqun@VM-0-9-ubuntu chapter4 % ./a.out                                                      [0]
    0
    0! = 1
    1
    1! = 1
    6
    6! = 720
    9
    9! = 362880
    ^C
    

    二、怎么去证明递归程序结果是否正确

    1.我们可以试着把每一次函数调用展开去分析。

    例如 6!在我们上述的代码中展开去分析如下:factor(6) = 6 * factor(5) = 6 * 5 * factor(4) = 6 * 5 * 4 * factor(3) = 6 * 5 * 4 * 3 * factor(2) = 6 * 5 * 4 * 3 * 2 * factor(1) = 6 * 5 * 4 * 3 * 2 * 1 = 720。

    但是你发现,这样去展开似乎有点“傻X”,我们应该有更好的证明方法。

    2.数学归纳法

    定义:

    数学归纳法(Mathematical Induction, MI)是一种数学证明方法,通常被用于证明某个给定命题在整个(或者局部)自然数范围内成立。

    原理

    最简单和常见的数学归纳法是证明当n等于任意一个自然数时某命题成立。证明分下面两步:
    1. 证明当n= 1时命题成立。
    2. 假设n=m时命题成立,那么可以推导出在n=m+1时命题也成立。(m代表任意自然数)
    这种方法的原理在于:首先证明在某个起点值时命题成立,然后证明从一个值到下一个值的过程有效。当这两点都已经证明,那么任意值都可以通过反复使用这个方法推导出来。把这个方法想成多米诺效应也许更容易理解一些。例如:你有一列很长的直立着的多米诺骨牌,如果你可以:
    1. 证明第一张骨牌会倒。
    2. 证明只要任意一张骨牌倒了,那么与其相邻的下一张骨牌也会倒。那么便可以下结论:所有的骨牌都会倒下。

    解题要点

    数学归纳法对解题的形式要求严格,数学归纳法解题过程中,
    第一步:验证n取第一个自然数时成立
    第二步:假设n=k时成立,然后以验证的条件和假设的条件作为论证的依据进行推导,在接下来的推导过程中不能直接将n=k+1代入假设的原式中去。
    最后一步总结表述。

    以我们上述的阶乘求法为例子。

    第一步:首先n = 1时,factor(1) = 1,结果时正确的,所以n=1时,代码逻辑正确。

    第二步:假设n = k时,factor(n) = m,由于(k + 1)!等于(k + 1)* k!,所以我们得出我们的程序递归程序设计正确。

    总结表述:

      有了上面的两步分析,我们就可以从factor(6)向下递推下去表示函数调用顺序,到了factor(1)后,就开始返回返回值,即向上回溯,如下图。

     

     这就是大致的验证递归程序设计是否正确的方法。

    三、递归程序的应用--欧几里得算法

    1.介绍 

      欧几里得算法又名辗转相除法,主要用于快速计算两个数字的最大公约数。

    定理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。

    形式化表示:假设a,b != 0时,gcd(a, b) = gcd(b, a % b).

    2.定理证明过程:

      要证明该定理,我们只需证明两个条件成立,分别为:

      1)若整数a,b的最大公约数为c,则b和a%b存在公因子c;

      2)b和a%b的公因子c是它们的最大公约数。

    2.1 证明1:

    过程:

      假设a,b的最大公约数为c,即gcd(a, b) = c。

      则 a = xc

        b = yc

      设a除以b为k,则a除以b的余数为a-kb,即a % b = a - kb

      a % b = a - kb

                = xc - kyc

                     = (x - ky)c

           由b = yc 和 a % b = (x - k)c,得知b和a % b都有一个公因子c。

      证明1完毕。

    2.2 证明2:

      证明2比较复杂一点,我们先来分析一下。首先我们由证明1已知gcd(b, a % b) = gcd(yc, (x - ky)c),若要证明c是b和a%b的最大公约数,则y和x-ky必须没有公因子,即y和x-ky互素,就是相当于证明gcd(y, x-ky) = 1。

    过程: 

      设gcd(y, x-ky) = d

      y = nd

           x - ky = md

    则可得 y = nd,x = md+ky = md+knd = (m + kn)d,又可推出a = (m + kn)dc,b = ndc,则gcd(a, b) = gcd( (m + kn)dc, ndc),又因为gcd(a, b) =  c,所以(m+kn)d和nd没有公因子,故d = 1,则gcd(y, x-ky) = 1,最后得出gcd(b, a%b) = gcd(yc, (x-ky)c) = c。

    3.用C语言编写一个求整数a,b的最大公约数的程序。

     1 /*************************************************************************
     2         > File Name: gcd.c
     3         > Author: yudongqun
     4         > Mail: qq2841015@163.com
     5         > Created Time: Mon 09 Nov 2020 05:06:35 PM CST
     6  ************************************************************************/
     7 #include <stdio.h>
     8 
     9 int gcd(int a, int b) {
    10     return (b ? gcd(b, a % b) : a);
    11 }
    12 
    13 int main(void) {
    14     int a, b;
    15     while (scanf("%d%d", &a, &b) != EOF) {
    16         printf("gcd(%d, %d) = %d
    ", a, b, gcd(a, b));
    17     }
    18     return 0;
    19 }

      编译并运行测试。

    ydqun@VM-0-9-ubuntu test % ./a.out                                                                    [0]
    3 2
    gcd(3, 2) = 1
    19 8
    gcd(19, 8) = 1
    16 8
    gcd(16, 8) = 8
    16 12
    gcd(16, 12) = 4
    12 16
    gcd(12, 16) = 4
    ^C
    ydqun@VM-0-9-ubuntu test %  
    

      完毕。

      

           

      

     

  • 相关阅读:
    类与对象
    最大子段和
    贪心-Wooden Sticks
    MFC改变坐标系
    Java集合不能存放基本数据类型
    关于lower_bound( )和upper_bound( )的常见用法
    数组乘法(大整数相乘)
    米勒罗宾素数测试法
    深度搜索--poj1321 棋盘问题
    memcpy复制整型数组相关
  • 原文地址:https://www.cnblogs.com/ydqblogs/p/13942652.html
Copyright © 2011-2022 走看看