java中的递归

本文探讨了数学中的递归函数概念,以非标准形式定义的函数f为例,展示了如何在Java中实现递归。强调了递归函数必须包含基准情形和递归推进两个基本法则,并通过计算f(x)来解释递归调用的过程。同时,提到了递归可能导致的错误,如无限递归和内存溢出。文章还讨论了递归设计的第三个法则,并以打印多位数为例说明递归的应用。最后,总结了递归的四个核心法则,提醒避免在数值计算中过度使用递归,因为它可能效率低下。
摘要由CSDN通过智能技术生成

有的时候,数学函数以不太标准的形式定义。例如,我们可以在非负数集上定义一个函数f,它满足f(0)=0且f(x)=2f(x-1)+x*x。
丛这个定义我们可以看到
f(1)=1,
f(2)=6,
f(3)=21,
以及
f(4)=58
当一个函数用它自己来定义时就称之为是递归的。
在java中允许函数是递归的。

但是值得记住的是,java仅仅是提供遵循递归思想的一种尝试。不是所有的数学递归函数都能被有效地(正确地)由java递归模拟来实现。

上面所说的递归函数f,只需要用几行简短的代码就能表示出来。

例如:
在这里插入图片描述
第10、11行处理基准情况,即就是此时的函数可以直接通过参数值的传递而算出,而不求助递归。

正如:f(x)=2f(x-1)+x*x若没有f(0)=0这个事实在数学上没有意义一样,java的递归方法若无基准情况也是毫无意义的。第13行执行的是递归调用

关于递归有几个重要的概念,不应该被混淆
1、递归是否就是遵循循环推理呢??
答案是:虽然我们定义一个方法用的是这个方法本身,但是我们并没有用方法的本身去定义一个方法的特定的实例。懂得人都懂。
例如:
在面向对象编程中,我定义javaBean实体类,我只是提供一个模版,并没有具体提供一个对象,我可以通过new关键字来创建一个新的对象

而在递归中特定的实例,就是一个特定的对象。

对于数值计算,通常使用递归不是一个好主意

那么递归调用是怎样进行执行运算的呢?

加入我们传递的参数是6
那么f(x)=2f(6-1)+ 66;

这个时候就需要我们知道f(5)=?

那么开始进行替换

那么就需要我们继续去找
f(4)…f(3)…f(2).一直找到f(0)
然后通过f(0)=0,确定f(1)=1,
通过f(1)=1,确定f(2)=6,
。。。。。。。。。。。
。。。。。。。。
最终得出f(6)=318
在这里插入图片描述

而实际上,递归调用将会反复进行知道出现基准情况,
例如当计算f(-1),那么会导致调用f(-2),f(-3)等等。由于这些情况不可能出现基准情况,因此程序也算不出答案,偶尔还会发生更微妙的错误。
以下是当我们求f(-1)发生的错误
在这里插入图片描述
当调用次数或者内存消耗达到一定程度,虚拟机会自动帮我们停止,并抛出异常。
下面的例子是一种微妙的错误:
在这里插入图片描述
错误在第13行上,因为把bad(1)定义为bad(1),。显然,实际上bad(1),究竟是多少,这个定义给不出任何线索,因此计算机会反复进行调用,以期望能解除他的值,最后计算机系统内存将占满,一般的情况下,我们会说该方法
在特殊的情况下是无效的,而在其他情形是正确的,但此处这么说则不正确,因为bad(2)调用bad(1),但是bad(1)解不出值来,因此bad2也解不出值来,bad3、4、5等都需要调用bad2,所以因为bad1解不出值来,其他的都解不出值来,除了0之外,这个程序对任何非负值都是无效的。对于递归程序,不存在像特殊情形的这样情况下。
上面的讨论导致递归的前2个基本法则:
1、基准情形。必须总要有某些特殊基准情形,他们不用递归就能求解
2、不断推进。对于那些要求递归求解的情形,递归调用必须总能够朝着一个基准情形推进。

现在给出我们为什么会有第三个设计法则

假设现在要打印输出一个n正整数
例如0…9…11…一直到正无向大
那么引入一个一个函数,就是我们的函数一次只能处理单个数字,并把它输出到屏幕终端上,当然0-9是最好输出的,直接传递参数即可
那么假如是11呢?,这是个2位数,假如是111呢,或者是1289这类数呢?
,值得我们注意的是,我们即使输入的数1289这样的数,其实我们不是通过一次输出而显示成功的,那么当我们要输出9的时候,那么9与1289又有什么关系呢,显而易见,是1289%10=9,那么128与1289又有什么关系呢---->是1289/10=128,那么由此可知,当我们执行1289/10得出128这个数,那么怎样得出12这个数,就是128/10=12, 1就是12/10=1,由此我们的循环定义成功了,因为我们找到了第一个要输出的数,那么第一个要输出的数是1-9
所以通过java编程实现过程就是如下操作:
public static void printOut(int n){
if(n>=10)
printOut(n/10);
printDigit(n%10);
}
3、设计法则。假设所有递归都能够运行

4、合成效益法则。在求解一个问题的一个实例时,切勿在不同的递归调用中重复性的工作。

使用递归计算例如斐波那契这类函数的值的想法不是一个好的注意,其道理正式根据第四条法则。

有了这些原理和法则,设计一个递归总算容易了许多。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值