day4递归原理及实现
递归
特定:
递归算法是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题十分有效,它往往是算法的描述简洁而且易于理解。
递归算法解决问题的特点:
(1)递归就是在过程或函数里调用自身。
(2)在使用递归测略时,必须有一个明确的递归结束条件,称为递归出口。
(3)递归算法解题通常显得很简洁,但递归算法解题的效率较低。所以一般不提倡递归算法设计程序。
(4)在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。
要求:
递归算法所体现的“重复”一般有三个要求:
一是每次调用在规模上都有所缩小(通常是减半);
二是相邻两次重复之间有密切的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
三是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。
下面来看一个简单的递归程序:
def calc(n):
print(n)
if n/2 >1:
res = calc(n/2)
#print语句是不会执行的,因为在执行的过程中,一致在调用函数,只有退出的时候return把返回值传递给res才会执行
print("res:",res)
print("N:",n)
#上面递归运算完成之后,把返回值返回给res,递归多少层,退出就有多少层
return n
calc(10)
运行如下:
10
5.0
2.5
1.25
N: 1.25
res: 1.25
N: 2.5
res: 2.5
N: 5.0
res: 5.0
N: 10
斐波那契数列指的是这样一个数列0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765 ......
def func(arg1,arg2,stop):
if arg1 == 0:
print(arg1,arg2)
arg3 = arg1 + arg2
print(arg3)
if arg3 < stop:
#把arg2,arg3作为新的参数传递给函数
func(arg2,arg3,stop)
func(0,1,30)
上面代码使用递归算法,每次调用函数并进行后面的数字相加,知道条件不满足位置,func(arg2,arg3,stop)是将arg2,arg3作为新的参数调用函数,此时函数重新执行,调用函数。
算法基础之二分法:
有一个有序列表,判断一个数是否在列表中,data = list(range(1,6000,3)),现在判断3001是否在列表中。
1.通过递归实现2分查找
现有列表primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],要求二等用最快的方式找出23。请Low B,Low 2B,Low 3B三个同学来回答这个问题。
Low B:这个问题很简单,直接用data.__contains__(23),语音未落就被老板打了,让你自己实现,不是让你用现成的功能,Low B于是说,那只能从头开始一个一个数了,然后Low B被开除了.....
Low 2B:因为这个列表是有序的,我们可以把列表从中截取一半,大概如下:
p1 = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,41]
p2 = [ 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
然后看p1[-1]也就是41是否比23大,如果比23大就代表肯定在p1里面,否则那就肯定在p2里面。现在我们知道23比41小,所以23肯定在p1里面,但p1里依然有很多元素,怎么找到23呢?很简单,依然按照上一次的方法,把p1分成2部分,如下:
p1_a = [2, 3, 5, 7, 11, 13,17]
p1_b = [19, 23, 29, 31, 37,41]
然后我们发现,23比p1_a最后一个值17大,那代表23肯定在p1_b中,p1_b中依然有很多元素,那就再按之前的方法继续分半,最终用不了几次,肯定就把23找出来了!
说完,Low BB满有成就感的甩了下头上的头皮屑。
老板说:很好,确实较Low B的方案强很多。然后转头就问Low 3B,你有更好的想法么?
Low 3B: 啊。。。噢 ,我。。。我跟Low 2B的想法一样,结果被他说了。
Low BBB:啊。。。噢 ,我。。。我跟Low 2B的想法一样,结果被他说了。
Low BBB此时冷汗直冒,因为他根本没思路,但还是硬着头皮去写了。。。。虽然自己没思路,但是会谷歌呀,三个小时过去了,终于憋出了以下代码:
def binary_search(data_source,find_n):
mid = int(len(data_source)/2)
if mid >= 1:
#递归必须有结束条件,这里的结束条件是当列表只有两个长度的时候运算终止
if data_source[mid] > find_n: #查找的值在中间值的左边
print("data in left of [%s]" %data_source[mid])
#print(data_source[:mid])
binary_search(data_source[:mid],find_n)
elif data_source[mid] < find_n:
print("data in right of [%s]" %data_source[mid])
#print(data_source[mid:])
binary_search(data_source[mid:],find_n)
else:
print("found find_n: ",data_source[mid])
else:
print("cannot finding....")
if __name__ == '__main__':
data = list(range(1,6000000,3))
#print(data)
binary_search(data,53)
上面代码中,我们的思想是这样的,如果我们直接23 in data的话,是遍历列表,列表多长就遍历多少次,我们可以使用二分法来解决,由于列表是有序的,我们可以先取中间值data_source[mid],拿这个值与我们要查找的值进行比较,如果data_source[mid]中间值大于查找的值,说明要找的值的范围在中间值的左侧;反之在中间值的右侧,如果与中间值相等,那么这个值就等于中间值,就不需要在查找了。以上过程进行递归反复,就能够找到哦我们所需的结论,我们知道,每次查找的过程中列表的长度都是减半的。递归要有结束条件,我们想,当列表只有两个值的时候就应该停止,没有必要在进行递归了,因为要么等于,要么不等于,如果等于就打印找到了,如果没有就打印找不到。
运行结果如下:
data in left of [3000001]
data in left of [1500001]
data in left of [375001]
data in left of [187501]
data in left of [93751]
data in left of [46876]
data in left of [23437]
data in left of [11719]
data in left of [5860]
data in left of [2929]
data in left of [1465]
data in left of [733]
data in left of [367]
data in left of [184]
data in left of [91]
data in right of [46]
data in left of [67]
data in left of [55]
data in right of [49]
found find_n: 52
上面代码我们就运行了20次就结束查找了,大大减少了遍历的次数,如果使用in需要遍历6000000次,所以使用一些简单的算法能够大大节约我们的时间。但是上面程序也有一个bug就是查找不到1,为什么呢?因为我们在判断的过程中,一直判断的是data_source[mid]>0,我们知道,那么如果我们要判断1,那么我们知道,没有比1更小的数,而上面代码比较的是data_source[data]大于、等于、小于find_n,如果满足则没有关系,如果不满足则提示查找不到,这是有明显缺陷的,因为没有判断最小的值,因为我们要对data_source[0]进行判断,如何判断呢?就只需要在mid == 1的时候判断即可,我们知道,在mid == 1的时候必然是只剩下两个元素了,只要我们判断两个元素的情况即可,判断data_source[0]和data_source[0]是否与查找的数相等,如果相等则返回查找到了,否则必然是查找不到的情况。修改后的代码如下:
def binary_search(data_source,find_n):
mid = int(len(data_source)/2)
if mid > 1:
#递归必须有结束条件,这里的结束条件是当列表只有两个长度的时候运算终止
if data_source[mid] > find_n: #查找的值在中间值的左边
print("data in left of [%s]" %data_source[mid])
#print(data_source[:mid])
binary_search(data_source[:mid],find_n)
elif data_source[mid] < find_n:
print("data in right of [%s]" %data_source[mid])
#print(data_source[mid:])
binary_search(data_source[mid:],find_n)
else:
print("found find_n: ",data_source[mid])
elif mid == 1:
if data_source[mid] == find_n or data_source[0] == find_n:
print("found find_n: ",find_n)
else:
print("cannot finding....")
# else:
# print("cannot finding....")
if __name__ == '__main__':
data = list(range(1,600,3))
#print(data)
binary_search(data,4)
上述代码中,我们加入了当mid == 1的时候对代码的判断,只要加上这么一个小小的判断就能够避免1查找不到的尴尬,修复了bug.
day4递归原理及实现的更多相关文章
- day4 递归原理及解析
递归 递归是一种调用自身的方法,在函数执行过程中重复不断的调用自身的过程,递归的规模每次都要缩小,一般前一步的程序作为后一步的参数.但是必须有递归结束条件. 递归算法是一种直接或者间接地调用自身算法的 ...
- python 装饰器、递归原理、模块导入方式
1.装饰器原理 def f1(arg): print '验证' arg() def func(): print ' #.将被调用函数封装到另外一个函数 func = f1(func) #.对原函数重新 ...
- TSql CTE 递归原理探究
CTE是如何进行递归的?产生递归的条件有三个,分别是 初始值 自身调用自身 结束递归的条件 1,示例代码 ;with cte as ( as jd union all as jd from cte ) ...
- JavaScript递归原理
JavaScript递归是除了闭包以外,函数的又一特色呢.很多开发新手都很难理解递归的原理,我在此总结出自己对递归的理解. 所谓递归,可以这样理解,就是一个函数在自身的局部环境里通过自身函数名又调用, ...
- Java中的递归原理分析
解释:程序调用自身的编程技巧叫做递归. 程序调用自身的编程技巧称为递归( recursion).递归做为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用 ...
- day4 递归二分法查找
现有一个序列,data=[for i in range(1,5000,3)],现在要求看一个数是否在列表中存在,我们知道,我们可以使用in或__contains__()的方法,判断一个值是否在列表中, ...
- python2.0 s12 day4
python2.0 s12 day404 python s12 day4 TengLan回顾上节内容 05 python s12 day4 迭代器原理及使用 本节大纲介绍: 1.迭代器&生成器 ...
- Java基础(49):快速排序的Java封装(含原理,完整可运行,结合VisualGo网站更好理解)
快速排序 对冒泡排序的一种改进,若初始记录序列按关键字有序或基本有序,蜕化为冒泡排序.使用的是递归原理,在所有同数量级O(n longn) 的排序方法中,其平均性能最好.就平均时间而言,是目前被认为最 ...
- SQL Server2005使用CTE实现递归
本文来自:http://www.cnblogs.com/wenjl520/archive/2010/01/18/1650393.html CTE递归原理: 递归CTE是由两个最小查询构建的.第一个是定 ...
随机推荐
- Udp打洞原理和源代码。
所谓udp打洞就是指客户端A通过udp协议向服务器发送数据包,服务器收到后,获取数据包,并且 可获取客户端A地址和端口号.同样在客户端B发送给服务器udp数据包后,服务器同样在收到B发送过来 的数据包 ...
- UVA-11582 数学
UVA-11582 题意: 求f[a^b]%n ,其中f是斐波那契数列,1<=n<=1000,0<=a,b<=2^64; 代码: //这题重点是要发现 f[i]%n会出现循环, ...
- 手脱PE Pack v1.0
1.PEID查壳 PE Pack v1.0 2.载入OD,一上来就这架势,先F8走着 > / je ; //入口点 -\E9 C49D0000 jmp Pepack_1.0040D000 004 ...
- table行拖拽
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 2017北京国庆刷题Day3 afternoon
期望得分:100+0+30=130 实际得分:100+36.5+0=136.5 T3 一个变量写混了,丢了30.. 模拟栈 #include<cstdio> #include<cst ...
- JVM调优总结(5):典型配置
以下配置主要针对分代垃圾回收算法而言. 堆大小设置 年轻代的设置很关键 JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理 ...
- java中集合去重1
//TreeMap 处理 处理后 得到的值 按拼音排序 TreeMap map = new TreeMap(new MaterialComparator()); map.put("东莞&qu ...
- 如何构建一个很棒网站页脚(Website Footer)
对于很多人来说,制作页脚是只是设计师顺手而为的任务.它似乎很容易,似乎可以忽略不计.因为很多人都觉得网站底部很少人会去看,而且页脚链接的所有链接的点击率(CTR)都是最低的,何必呢? 真是这样的吗?下 ...
- R1(上)—R关联规则分析之Arules包详解
Arules包详解 包基本信息 发布日期:2014-12-07 题目:挖掘关联规则和频繁项集 描述:提供了一个表达.处理.分析事务数据和模式(频繁项集合关联规则)的基本框架. URL:http://R ...
- 【BZOJ】1176: [Balkan2007]Mokia
[题意]n*n的矩阵,初始值为0(题面有误),m次操作,增加一个格子的权值,或查询子矩阵和.n<=2*10^6.(m应该较题面所述偏大). [算法]CDQ分治(算法知识见数据结构) [题解]三维 ...