用类比方式学习编程中函数递归(个人理解仅供参考)(内含汉诺塔问题的求解)

news2024/9/29 13:27:20

目录

1.前言

2.递归的数学模型

3.相关的c语法

4.将递归的数学模型写成编程语言

5.利用类比方法将实际问题的代码写成函数递归的形式

例1:

 例2:

6.汉诺塔问题的求解


1.前言

本人在学习函数递归编程方法的过程中,发现用类比的方式学习递归法可帮助我们在各种编程问题中将函数递归法运用于代码设计中。

在我看来,函数递归最底层的思维模型是:数学中的数列的递推公式(数列的递推公式是研究数列性质和求通项公式的核心,将递推迭代思维模型应用于代码设计中,就产生了函数递归).

2.递归的数学模型

现在我们任意给出的一个数列 {An} 的首项和它的递推公式:

A1 == 1, An ==  2*A(n-1) +  1 (n>1)

在数学中求解该数列的通项公式的方法有很多种,其中有一种叫做迭代法:(迭代法可以用于求解各种递推公式为一阶递推式的数列的通项) 

所谓迭代法就是将递推公式中的项按照递推公式本身逐项展开(即从An开始一直逐项递值展开到A1,然后通过计算返回出结果

接下来我们用迭代法来试求{An}的通项公式: (n>1)

An=2*A(n-1) + 1 = 2*(2*A(n-2) +1) +1 = 2*(2*(2*A(n-3)+1)+1) +1=................................

以这种方式一直展开到A1再通过归纳整理便可以得出An的通项公式.

3.相关的c语法

我们知道,计算机最擅长做的事情就是重复大量地进行同一形式的计算。然而在迭代法中,我们从An开始按照 同一个递推公式 将其逐项展开直到A1这种算法显然十分符合计算机的思维模式.在C语言中我们允许函数嵌套自身即:       

int  func(int n)    
{
    func(n); 
    return 0;
}

     

调用函数的时候 函数本身在返回输出之前会先调用自身并且这样的过程会不断重复进行

为了能够最终让函数返回输出,我们要对函数向内递值进行条件约束 并且每次向内传递的值都要逼近约束条件比如:

   


int func(int n) 
{
     if(n>=1)
     { 
        func(n-1);
     }
     else
     {
        return0;
     }
}

 这样的话函数就会逐层向内递值n次后再逐层返回输出. 

4.将递归的数学模型写成编程语言

现在我们想让计算机帮我们求数列{An}的任意一项的值.要求设计一个函数int func(int)输入正整数n作为形参,函数便返回An的值.

思路引导:{An}的递推公式 An= 2*A(n-1)  + 1    

如果递推公式中An相当于代码中的 func(n),那么A(n-1)则相当于代码中的func(n-1)

结合迭代法求通项的思路和相关C语法便可写出如下代码

int func(n)
{
   if(n>1)                  //递推公式的成立条件
   {
      return 2*func(n-1)+1; //数列的递推公式
   } 
   else
   {
      return 1 ;            // 数列首项为以1
   }
}

计算机在执行这段代码时就会自动帮助我们完成迭代的过程.(该过程中函数减值向内递值直到最后一次向内递入整数1【“递”过程】,再从最内层函数开始逐层向外返回数值【“归”过程】并最终得到结果)(递推公式决定函数递值的方式和函数结构,首项决定归值节点

进一步思考可以得知,任意给定一个数列,只要我们知道其首项和递推公式,我们就可以写出与上面代码形式相同的代码用于求任意项的数值.

5.利用类比方法将实际问题的代码写成函数递归的形式

例1:

问题:设计一个函数代替库函数strlen()  ,用于求字符串长度,形参是char*类型 返回值是int类型.

我们自定义函数取名叫做restrlen() 

假设我们要求字符串“bite”的长度. 创建字符串数组char arr[]="bite".

先抽象出一个数列的某一项 restrlen("bite")

我们可以得到递推关系 restrlen("bite") = restrlen("ite") + 1

鉴于题目要求,求“bite”长度时我们只能将arr即字母b的地址传入函数中

那么restrlen("bite")就相当于restrlen(arr),类似地,restrlen("ite")就相当于restrlen(arr+1)

于是递推关系就变成了 restrlen(arr)=restrlen(arr+1) +1  

该问题中首项是restrlen(arr+x) = 0 , x为某一整数 并且满足*(arr+x)="\0"  于是我们便有了如下代码:

int restrlen(char* x)
{
    if(*(x+1)!=0)                  //类似于递推公式成立条件
    {
         return restrlen(x+1) +1;  //类似于数列递推公式
    }
    else
    {
         return 0;                 //类似于首项
    }
}

 例2:

问题:设计一个函数当输入一个整数时,函数会依次输出打印出该整数每一个十进制位上的数字如:输入123 打印1 2 3

我们给函数取名叫做 prt 以输入123为例子

要先打印出1 那么1肯定在最内层函数完成输出(归值从内而外)

先抽象出一个数列的项prt(123) 其前一项可以抽象为prt(12)

那么我们便可以抽象出其递推公式  prt(123)= prt(12) + "printf("%d",123 %10)

如果数列的中n相当于“123”,那么n-1则相当于“123/10”

当输入值整除10等于0时就得到“首项” (类比意义上) “首项”的值为0

由于函数输出方式为打印输出所以无需返回值(注意要先向内层递值再打印输出)

void Print(int n)
{
   if(n/10!=0)
   {
       Print(n/10);
       printf("%d",n%10);
   }
}

6.汉诺塔问题的求解

先从数学角度对汉诺塔问题进行分析:现在设 将A柱上n个圆盘借助B柱移动到C柱 需要移动圆盘的总次数为Xn.
显然我们可以得到数列{Xn} ,其中n>=1     接下来我们来研究该数列的递推公式 即尝试研究Xn与Xn-1的关系
从特殊到一般的方法对问题进行分析 我们可以分析出如下规律;

  1. 为了将A柱上n个圆盘借助B柱移动到C柱,我们必须经历这样一个步骤:将A柱上最大的圆盘移动要C柱上.
  2. 在完成1.分析中所述的步骤之前,A柱上必须只剩一个最大的圆盘,C柱必须的空的,此时B柱上则有n-1个圆盘
  3. 因此我们必须在游戏开始后先完成另外一个步骤:将A柱上n-1个圆盘借助C柱移动到B柱上.
  4. 我们称分析3.中所述步骤为:第一步骤(我们无需理会该步骤具体是如何进行的)称分析1.中所述步骤为:第二步骤
  5. 很显然若Xn代表n个圆盘的汉诺塔问题中需要移动圆盘的总次数,那么第一步骤需要移动圆盘的总次数为Xn-1次
  6. 同时,第二步骤需要移动圆盘的总次数为1次
  7. 在完成了第一步骤和第二步骤后我们便可以无视C柱上那个最大的圆盘接着分析下一步
  8. 接下来,我们只需 将B上的n-1个圆盘借助A柱移动到C柱 我们称这一步为 第三步骤(我们同样无需理会该步骤具体是如何进行的)
  9. 显然完成第三步骤所需移动圆盘的总次数同样为Xn-1 次

通过上述分析我们可以得到结论:为了解决n(n>1)个圆盘的汉诺塔问题我们需要经历如下三个步骤(设总移动圆盘的次数为Xn) (这里A为起始柱,B为过渡柱,C为目标柱)
第一步骤:将A柱上n - 1个圆盘借助C柱移动到B柱上(移动Xn-1次)(这里A为起始柱,C为过渡柱,B为目标柱)
第二步骤:将A柱上剩下的那个最大的圆盘移动要C柱上(移动1次)   
第三步骤:将B上的n - 1个圆盘借助A柱移动到C柱(移动Xn-1次)(这里B为起始柱,A为过渡柱,C为目标柱)
即得到数列的递推公式(n>1):      

          Xn       =     ( Xn-1 )   +   (1)   +   (Xn-1)
  /*移动圆盘的总次数*/    /*第一步骤*/ /*第二步骤*/   /*第三步骤*/

当n==1时显然我们只需执行一次第二步骤 即X1==1

利用上面的数学结论我们来尝试解决汉诺塔的c语言编程问题
编程的要求是:设计一个函数 输入形参n作为初始圆盘总数 函数(比如输入n==3时)以如下的形式打印输出(以这种形式告诉用户解决n个圆盘的汉诺塔问题的每个具体的圆盘移动步骤)

               A-->C        
               A-->B
               C-->B
               A-->C
               B-->A
               B-->C
               A-->B

根据数学递推公式的分析我们可以初步得出:汉诺塔函数中我们有两个减值向内层函数递值的步骤(“递”步骤) 中间还有一个移动单个圆盘的步骤
很显然中间那个移动单个圆盘的步骤可以作为我们函数递归的返回输出节点(即从最内层函数开始向外层函数返回输出的“归”步骤)
于是我们可以得到如下函数递归框架:

    void Hanoi(int n)     //总过程:将A柱上n个圆盘借助B柱移动到C柱
    {
        Hanoi(n - 1);     /*第一步骤(函数向内递值 即“递”的步骤)*/
        printf() ;        /*第二步骤(该步骤具体实行打印输出 即"归"的步骤)*/
        Hanoi(n - 1);     /*第三步骤(函数再一次向内递值)*/
    }

以上便是根据汉诺塔问题数学分析得出的基本递归框架
然而为了将 《最外层函数的“将A柱上n个圆盘借助B柱移动到C柱”过程》以及《第一个内层函数的“将A柱上n - 1个圆盘借助C柱移动到B柱上”过程》以及《第二个内层函数的“将B上的n - 1个圆盘借助A柱移动到C柱”过程》  三者区分开来
我们必须引入三个字符形参代表A B C三个柱子 并且通过这三个字符形参在函数小括号()中的排列顺序的区别来区分前句分析的三个过程
于是我们可以进一步填充函数递归框架 并给出函数向内递值的限制条件完成代码设计

int count = 0;
void Hanoii(char a, char b, char c, int n)          
{
    if (n >= 1)
    {  
        Hanoii(a, c, b, n - 1);                     
        count++;
        printf("%d.  %c-->%c\n", count, a, c);      
        Hanoii(b, a, c, n - 1);                      
                                                    
    }
}
int main()
{
    char x = 'A';
    char y = 'B';
    char z = 'C';
    int k = 0;
    scanf("%d", &k);
    Hanoii(x, y, z, k);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day14_类中成员之一:构造器

由来 我们发现我们new完对象时,所有成员变量都是默认值,如果我们需要赋别的值,需要挨个为它们再赋值,太麻烦了。我们能不能在new对象时,直接为当前对象的某个或所有成员变量直接赋值呢。可以,Java给我们提…

【算法】二分

作者:指针不指南吗 专栏:算法篇 🐾或许会很慢,但是不可以停下来🐾 文章目录1.二分思想2.二分模板3.二分应用1.二分思想 思想 单调的元素,一定可以二分;非单调不一定不能二分 每次把整个区间 [ …

【企业云端全栈开发实践-1】项目介绍及环境准备、Spring Boot快速上手

本节目录一、 项目内容介绍二、Maven介绍2.1 Maven作用2.2 Maven依赖2.3 本地仓库配置三、Spring Boot快速上手3.1 Spring Boot特点3.2 遇到的Bug:spring-boot-maven-plugin3.3 遇到的Bug2:找不到Getmapping四、开发环境热部署一、 项目内容介绍 本课程…

肿瘤HRR和HRD 简单记录

最近看到两个在肿瘤领域高频出现的词HRR和HRD, 遂简单记录下。 HRR和HRD的概念 当细胞受到外界不良环境的压力下往往会导致DNA的损伤,此时便会触发DNA 损伤反应 (DDR),从而激活许多DNA修复通路。在这些DNA损伤中,DNA 双链断裂&a…

【云原生】centos7搭建安装k8s集群 v1.25版本详细教程实战

文章目录前言一. 实验环境二. k8s 的介绍三 . k8s的安装3.1 搭建实验环境3.1.1 硬件层面的要求3.1.2 软件层面环境配置3.2 docker的安装3.2.1 搭建docker3.2.2 部署 cri-dockerd3.3 部署k8s3.3.1 配置添加阿里云的yum源3.3.2 安装kubeadm kubelet kubectl3.3.3 k8s-master节点初…

浪涌保护器,防雷浪涌保护器的作用和类型指南

1. 什么是SPD浪涌保护器?地凯防雷SPD浪涌保护器是防止雷击导致故障的避雷器和浪涌保护设备。广泛用于电源浪涌对策的变阻器在通电超过规格的雷电浪涌电流、超过最大容许电路电压的过电压、过电流时,会进入短路故障模式,存在冒烟起…

Mysql元数据获取方法(information_schema绕过方法)

前提:如果waf或其它过滤了information_schema关键字,那我们该如何获取元数据呢?能够代替information_schema的有:sys.schema_auto_increment_columnssys.schema_table_statistics_with_bufferx$schema_table_statistics_with_buff…

大数据框架之Hadoop:HDFS(七)HDFS 2.X新特性

7.1集群间数据拷贝 scp实现两个远程主机之间的文件复制 ​ scp -r hello.txt roothadoop103:/root/hello.txt // 推 push ​ scp -r roothadoop103:/root/hello.txt hello.txt // 拉 pull ​ scp -r roothadoop103:/root/hello.txt roothadoop104:/root //是通过本地主机中…

计算机技术与软件(初级、中级、高级)考试(软考)是什么?软考的时间安排是什么时候?

一、软考是什么? 计算机技术与软件专业技术资格(水平)考试(以下简称计算机软件资格考试)是原中国计算机软件专业技术资格和水平考试(简称软件考试)的完善与发展。计算机软件资格考试是由国家人力…

Fluent工作目录

1 工作目录定义工作目录(working directory)是一种文件存储路径设置方式。基于工作目录的方法,写文件时只需要指定文件名,而不需要指定完全的文件路径,从而简化程序编写,对不同操作系统环境有更好的适应性。…

#笨鸟先飞# 数据结构与算法基础 课程笔记 第六章 图

图的定义和基本术语图:G( V , E ) Graph(Vertex,Edge)V:顶点(数据元素)的有穷非空集合;E:边的有穷集合。无向图:每条边都是无方向的有向图:每条边…

新手小白入门必看!如何批量注册Twitter账号?

Twitter是目前海外比较流行的社媒营销平台,所以很多从事跨境电商行业的朋友都需要利用多个Twitter账号来推广营销,但是注册和管理多个Twitter账号其实并不是简单的事情。龙哥将会在这里详细讲讲该如何批量注册并且让这些账号不会因为关联被封号&#xff…

如何合理地制定项目管理计划?

甘特图是一个比较常见的直观地项目管理专用工具,我们可以使用用Excel制作表格,也可是使用项目管理软件。 详细的项目计划不能完全保证项目成功,但是可以将失败的风险降到最低。通过项目管理软件的图标,项目管理者可以直观的查看任…

字符串的特殊读取——基于蓝桥杯两道题目(C/C++)

目录 1 例题 1.1 卡片换位 1.2 人物相关性分析 2 字符串的读取 2.1 综述 2.2 scanf 2.3 getline/getchar/get 2.4 注意 2.5 说明 先看例题 1 例题 1.1 卡片换位 问题描述 你玩过华容道的游戏吗? 这是个类似的,但更简单的游戏。 看…

加拿大访问学者如何提升自我

学习加拿大先进的科学技术和研究方法是我们访学的最主要目的。(1)根据自己的先前的研究计划、加方导师的项目和科研条件尽早调整自己目前学习和研究。(2)可以适当、有选择地听一些相关的课程,在提高英语水平的同时,又能够了解外国的教学方法。(3)与导师积…

pytest+yaml+allure接口自动化测试框架04.处理request

前言 ​ 环境搭建配置、封装日志缓存、读取yaml测试文件,这些工作我们上几个章节已经都做完了,读取文件之后,我们已经成功拿到了测试yaml文件中的测试数据了,那我们接下来就通过这些数据去执行测试,主要就是对这些数据…

SpringBoot+Vue实现酒店客房管理系统

文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏…

异步 Servlet

1.什么是异步 Servlet 在 Servlet3.0 之前,Servlet 采用 Thread-Per-Request 的方式处理 Http 请求,即每一次请求都是由某一个线程从头到尾负责处理。 如果一个请求需要进行 IO 操作,比如访问数据库、调用第三方服务接口等,那么其…

云原生丨一文教你轻松借助DEX实现单点登录~

文章目录前言一、分析思路1、单点登录授权码认证隐式认证混合认证2、会话管理二、实现过程1、搭建DEX认证中⼼2、登录流程说明授权码认证示例代码3、登出流程说明登出代码示例前言 通常,我们在登录单系统时,都希望只需要登录⼀次,就能访问本…

分享一下最近使用python字典取值用法的收获

假设现在有一个字典,内容如下:data {a: 1, b: 2}初级版本 我最开始学python的时候, 要从字典中取值,我可能会采用下面的写法:print(data["key"])上面的用法中,如果输入的key在字典中不存在的时候…