浅谈python中的递归

python 浅谈 递归函数
最近在自学一些python,找了些资料。自己慢慢研究到了递归函数这一章,碰到个很经典的例子。汉诺塔的移动。一开始尝试自己写的时候发现,这东西怎么可能写的出来。但是看到别人写出来以后发现,这东西真的能写出来。
本着借鉴的目的想去分析一下别人写的东西。觉得很有意思想给大家分享一下,如果有误请大家指正首先大家可以先自己想想如何能写出来。
先说一下:所谓的递归,我认为就是不断重复调用。直到return 出当前的递归循环。在我拆分的过程中,大家不妨先自己想一下结果,然后看一下我执行出来的结果,是否和各位所想的一样呢。
本文仅代表自己观点,如有错误大家指出。

	--------------python 环境为3.6.4-------- 下面为高手写的代码
def move(n, a, b, c):
	if n == 1:
		return print(a, ‘-->‘, c)
	move(n-1,a,c,b)
	move(1,a,b,c)
	move(n-1,b,a,c)
----------------------------------------------
	乍一看真的是一个很简单很简单的三层递归函数。我第一眼看到的时候都不认为这个是对的。但是真的执行以后发现。写的很有道理。输入2和3执行一下。发现都能出来。
>>> move(2,‘a‘,‘b‘,‘c‘)
a --> b
a --> c
b --> c
>>> move(3,‘a‘,‘b‘,‘c‘)
a --> c
a --> b
c --> b
a --> c
b --> a
b --> c
a --> c

直接看,我的水平着实有限,所以打算拆开来一步步看。首先改写一下格式。我准备一个一个拆分来看。首先将第一个递归拆出来。带入几个值看一下
def move(n, a, b, c):
    if n == 1:
        return print(a, ‘-->‘, c)
    move(n-1,a,c,b)
>>> move(2,‘a‘,‘b‘,‘c‘)
a --> b
>>> move(3,‘a‘,‘b‘,‘c‘)
a --> c
>>> move(4,‘a‘,‘b‘,‘c‘)
a --> b
>>> move(5,‘a‘,‘b‘,‘c‘)
a --> c
发现了一个很有意思的结果,就是在输入值(n>=2)且n为偶数的时候 是 a --> b,输入值为奇数的时候 是 a --> c。原因也很简单。在这个函数做递归的时候,因为n不等于1,所以递归函数一直在自己调用自己。所以字符 b和c的位置一直在互换。  

继续拆分。
def move(n, a, b, c):
    if n == 1:
        return print(a, ‘-->‘, c)
    move(1,a,c,b)
>>> move(2,‘a‘,‘b‘,‘c‘)
a --> b
>>> move(3,‘a‘,‘b‘,‘c‘)
a --> b
>>> move(4,‘a‘,‘b‘,‘c‘)
a --> b
因为,这个n值为定值1.所以无论n(n>=2)值输入的值为何值都只会输出 a --> b。

最后拆分最后一个递归函数来看,
def move(n, a, b, c):
	if n == 1:
		return print(a, ‘-->‘, c)
	move(n-1,b,a,c)
	根据之前的两个可以想象得出来,这个函数在n(n>=2)输入奇数和偶数的时候输出值一定不一样。这里不做多解释。
>>> move(2,‘a‘,‘b‘,‘c‘)
b --> c
>>> move(3,‘a‘,‘b‘,‘c‘)
a --> c
>>> move(4,‘a‘,‘b‘,‘c‘)
b --> c
>>> move(5,‘a‘,‘b‘,‘c‘)
a --> c

接着如果两两结合呢。
执行 n=2、3、4的结果分别为:
def move(n, a, b, c):
    if n == 1:
        return print(a, ‘-->‘, c)
    move(n-1,a,c,b)
	move(1,a,b,c)
>>> move(2,‘a‘,‘b‘,‘c‘)
a --> b
a --> c
>>> move(3,‘a‘,‘b‘,‘c‘)
a --> c
a --> b
a --> c
>>> move(4,‘a‘,‘b‘,‘c‘)
a --> b
a --> c
a --> b
a --> c
一旦开始两两结合。立马开始变得复杂了,这里有如下几种可能:
1 顺序执行,即传输一个值进去,会完整的走一次函数。
如果传输值为2,执行move(2-1,a,c,b) return ,move1。输出结果为如上所示。
如果传输值为3,则执行为,move(3-1,a,c,b) pass, move1, move(2-1,a,c,b) return, move(1,a,b,c)。 输出结果为如上所示
如果传输值为4,则执行为,move(4-1,a,c,b) pass,move1,move(3-1,a,c,b),move1,move(2-1,a,c,b) return,move(1,a,b,c)。 这里很明显输出结果并不是这样的。所以并不是顺序执行这种。

2 组内循环,即传输一个n值进去,在带有n值运算的条件中,一直循环调用到能return时再跳出当前循环。我认为这种方式应该是正确的。(句末带--为输出行,代码中abc为实际代表的abc)
如输入的n为2,则
move(2,a,b,c)
	move(1,a,c,b)--return跳出当前循环,abc值为上一层所变化的值
move(1,a,b,c)--
输出结果为: a-b a-c
输入的n为3,则
move(3,a,b,c)
	move(3-1,a,c,b)
		move(3-1-1,a,b,c)--return跳出当前循环,abc值为上一层所变化的值
	move(1,a,c,b)--
move(1,a,b,c)	--
输出结果为 : a-c a-b a-c
输入n为4,则
move(4,a,b,c)
	move(4-1,a,c,b)
		move(4-1-1,a,b,c)
			move(4-1-1-1,a,c,b)-return跳出当前循环,abc值为上一层所变化的值
		move(1,a,b,c)--
	move(1,a,c,b)--
move(1,a,b,c)--
输出结果为:a-b a-c a-b a-c
这类型的递归类似于一个凹字。

两两结合还有一种情况就是:这类的循环是先做一次 move1 然后做 move n 时会去调用move1。结果如下所示。
def move(n, a, b, c):
    if n == 1:
		return print(a, ‘-->‘, c)
	move(1,a,b,c)
    move(n-1,a,c,b)
执行 n=2、3、4的结果分别为:
>>> move(2,‘a‘,‘b‘,‘c‘)
a --> c
a --> b
>>> move(3,‘a‘,‘b‘,‘c‘)
a --> c
a --> b
a --> c
>>> move(4,‘a‘,‘b‘,‘c‘)
a --> c
a --> b
a --> c
a --> b

这种情况下如果n为2
move(2,a,b,c)
	move(1,a,b,c)--
	move(2-1,a,c,b)--
输出结果为:a-c a-b
n为3则
move(3,a,b,c)
move(1,a,b,c)--
	move(3-1,a,c,b)
		move(1,a,c,b)--
			move(3-1-1,a,b,c)--
输出结果为 a-c a-b a-c
n为4则
move(4,a,b,c)
	move(1,a,b,c)--
	move(4-1,a,c,b)
		move(1,a,c,b)--
			move(4-1-1,a,b,c)
				move(1,a,b,c)--
					move(4-1-1-1,a,c,b)--
输出结果为:a-c a-b a-c a-b
这类型的递归类似于一个不断增加的直线(语言很笨拙)

两两结合最后一种情况如下所示:这类递归中,会先调用n-1,然后,后续函数在当前循环中使用n-1后的值。一组递归函数完成之后。重新调用第二组时,会重新初始化输入值。
def move(n, a, b, c):
    if n == 1:
            return print(a, ‘-->‘, c)
    move(n-1,a,c,b)
    move(n-1,b,a,c)
>>> move(2,‘a‘,‘b‘,‘c‘)
a --> b
b --> c
>>> move(3,‘a‘,‘b‘,‘c‘)
a --> c
c --> b
b --> a
a --> c
>>> move(4,‘a‘,‘b‘,‘c‘)
a --> b
b --> c
c --> a
a --> b
b --> c
c --> a
a --> b
b --> c

如果输入的n为2
move(2,a,b,c)
	move(2-1,a,c,b)--循环1
	move(2-1,b,a,c)--循环2
输出结果为: a-b a-c
如果输入的n为3
move(3,a,b,c)
	move(3-1,a,c,b)
		move(3-1-1,a,b,c)--
	move(3-1-1,b,a,c)--循环1结束
	move(3-1,b,a,c)#2组循环开始循环2为2时
		move(3-1-1,b,c,a)--
	move(3-1-1,a,b,c)--
输出结果为:a-c b-c b-a c-a
如果输入的n为4
move(4,a,b,c)
	move(4-1,a,c,b)
		move(4-1-1,a,b,c)
			move(4-1-1-1,a,c,b)--
		move(4-1-1-1,b,a,c)--
	move(4-1-1,c,a,b)
		move(4-1-1-1,c,b,a)--
	move(4-1-1-1,a,c,b)--至此循环1结束
	move(4-1,b,a,c)#2组循环开始循环2为3时
		move(4-1-1,b,c,a)
			move(4-1-1-1,b,a,c)--
		move(4-1-1-1,c,b,a)--
	move(4-1-1,a,b,c)#组循环开始循环2为2时
		move(4-1-1-1,a,c,b)--
	move(4-1-1-1,b,a,c)--
输出结果为:a-b b-c c-a a-b b-c c-a a-b b-c
这类递归类似于波形。	

个人认为我这种想法应该是对的,下面是我自己突发奇想想到的一个验证我的这个想法的代码。这个代码中可以观察n的各个阶段中各个值的实际值。
def move(n, a, b, c):
    print(n,a, b, c)
    if n == 1:
        return print(a, ‘-->‘, c)
    move(n-1,a,c,b)
    move(1,a,b,c)
这个代码可以在任意步骤显示,a,b,c中的值。可以参考。

接下来就是三个递归函数的调用了。根据两个递归函数的调用来猜测一下。
def move(n, a, b, c):
	if n == 1:
		return print(a, ‘-->‘, c)
	move(n-1,a,c,b)
	move(1,a,b,c)
	move(n-1,b,a,c)
>>> move(2,‘a‘,‘b‘,‘c‘)
a --> b
a --> c
b --> c
>>> move(3,‘a‘,‘b‘,‘c‘)
a --> c
a --> b
c --> b
a --> c
b --> a
b --> c
a --> c
如果n的传输值为2时
move(2,a,b,c)
	move(2-1,a,c,b)--
	move(1,a,b,c)--
	move(2-1,b,a,c)--

如果n的传输值为3时
move(3,a,b,c)
	move(3-1,a,c,b)
		move(3-1-1,a,b,c)--
	move(1,a,c,b)--
	move(3-1-1,c,a,b)--
move(1,a,b,c)--
	move(3-1,b,a,c)
		move(3-1-1,b,c,a)--
	move(1,b,a,c)--
	move(3-1-1,a,b,c)--
详细说一下这个吧:根据之前所说的,
1.首先带入(3,a,b,c)进入递归调用,先走第一个小循环move(n-1,a,c,b),此时n=3。
小循环中走n=3-1条件不满足,继续走小小循环n=3-1-1 return,得出结果,退出当前小小循环。
用当前小循环中的值走(1,a,b,c)输出一个值,然后用当前的n=2走小循环中的move(n-1,b,a,c)。走完之后。第一个小循环整体走完。
2.然后在整体递归函数中走move(1,a,b,c)。
3.然后开始move(n-1,b,a,c)的递归调用,同1类似。

move(4,a,b,c)
	move(4-1,a,c,b)
		move(4-1-1,a,b,c)
			move(4-1-1-1,a,c,b)--
		move(1,a,b,c)--
		move(4-1-1-1,b,a,c)--
	move(1,a,c,b)--
	move(4-1-1,c,a,b)
		move(4-1-1-1,c,b,a)--
	move(1,c,a,b)--
	move(4-1-1-1,a,c,b)--
move(1,a,b,c)--
	move(4-1,b,a,c)
		move(4-1-1,b,c,a)
			move(4-1-1-1,b,a,c)--
		move(1,b,c,a)--
		move(4-1-1-1,c,b,a)--
	move(1,b,a,c)--
	move(4-1-1,a,b,c)
		move(4-1-1-1,a,c,b)--
	move(1,a,b,c)--
	move(4-1-1-1,b,a,c)--

以上是n=4时汉诺塔的具体执行步骤。我自己能写明白。讲就算了。大家自行理解吧。
写在最后,可能我的语言表述并不是特别好,但是步骤写的很清楚了。每一次的递归,递归调用递归,递归调用常函数。常函数调递归。以及各个阶段值的变换。
因为我也才接触python没多久。都是自己在摸索。各位如有问题请及时联系我。我再进行相应的修改。

  

原文地址:https://www.cnblogs.com/newxiaobai/p/8296368.html

时间: 2024-11-05 12:24:41

浅谈python中的递归的相关文章

浅谈Python中的编码规则

注:本人用Python3.4作为学习版本,以下学习心得只适用于Python3.4. 之前拜读了金角大王Alex关于编码的解答,收获颇多.特此致谢,以下仅谈一谈作为一个初学者,对编码的理解. 我所了解的编码,大致分为两类:第一类是支持中文的编码集:第二类是支持英文的编码集.至于别国的编码集,暂且不做讨论. 常见编码:ASCII:Unicode:UTF-8:big5,:GB2312:GBK:GB18030 接下来,我对以上编码进行分类: 只支持英文和特殊字符的编码:ASCII ASCII是基于拉丁字

浅谈python中的一般方法、静态方法(staticmethod)和类方法(classmethod)

我们先来简单谈谈python类中一般方法.静态方法和类方法的区别. 1.类中的一般方法 一般方法在定义的时候,需要有表示类实例的参数(通常以self表示,例如def foo(self,arg1,arg2--)),一般方法不能通过类名.方法名()来调用,必须先创建类的实例,然后通过实例.方法名()来调用. 2.类中的静态方法 类中的静态方法用"@staticmethod"来修饰,静态方法在定义的时候,不需要表示类的实例,可想类外的方法定义一样.静态方法可以通过类名.方法名()和实例.方法

浅谈python中得import xxx,from xxx import xxx, from xxx import *

在python中import跟from import都是用来导入的,但是导入的机制不同 1.import xxx:导入模块,或者文件夹,对于调用模块或者文件夹中子模块的变量或者函数,需要使用"模块".XX来调用 2.from xx import xx:导入的是函数或者变量,类,可以直接使用xx 3.from xx import *:导入xx中得全部"公开"变量,属性,公开指的是不是以"_"开头,除非模块或包中的"__all__"

浅谈python中处理时间的模块

我们知道,Python提供了三种时间函数,时间模块 time.基本时间日期模块 datetime 和日历模块 Calendar. 一.time模块 1.在time模块中,可以用三种表现形式来表示时间,分别是时间戳.格式化时间字符串和结构化时间: 1).时间戳,通过time.time()获得 >>> time.time() 1545027641.4434128 2).格式化时间字符串,通过类似于time.strftime("%Y-%m-%d %H:%M:%S")这样的表

浅谈python中字典

1.字典的定义方式有以下: a=dict(one=1,two=2,three=3) b={'one':1,'two':2,'three':3} c=dict(zip(['one','two','three'],[1,2,3])) d=dict([('two',2),('one',1),('three',3)]) e=dict({'three':3,'one':1,'two':2}) 其中这四种定义方式完全等效,有一点需要牢记,标准库中所有的映射类型都是通过字典(dict)来实现,其中只有可散列(

浅谈python中的“ ==” 与“ is”、还有cmp

总之,比较内容相等使用 '==' 1.is" 是用来比较 a 和 b 是不是指向同一个内存单元,而"=="是用来比较 a 和 b指向的内存单元中的值是不是相等 2.python有一个叫小整数对象池的东西,python为了优化速度,会把[-5,256]之间的数据提前存放在小整数对象池中,程序中只要用的[-5,256]之间的数据不会再重新创建一份,都是指向对象池中的同一份数据,除了这个区间之外的整数数据,每次使用时系统都会重新申请一块内存,用来存储数据,这样之前的现象也就不奇怪了

浅谈Python中with(上下文管理器)的用法

例子一 首先来看一段代码: class Foo(object): def __init__(self): print('实例化一个对象') def __enter__(self): print('进入') def __exit__(self, exc_type, exc_val, exc_tb): print('退出') obj = Foo() with obj: print('正在执行') 上面代码执行结果为: 实例化一个对象 进入 正在执行 退出 结论1 我们知道,实例化Foo,得到obj对

浅谈python中selenium库调动webdriver驱动浏览器的实现原理

最近学web自动化时用到selenium库,感觉很神奇,遂琢磨了一下,写了点心得. 当我们输入以下三行代码并执行时,会发现新打开了一个浏览器窗口并访问了百度首页,然而这是怎么做到的呢? 1 from selenium import webdriver 2 driver = webdriver.Chrome() 3 driver.get('http://www.baidu.com') 首先我们来看一下selenium库的结构: 很显然,selenium就是一个软件包,里面有两个一级子包,commo

浅谈python中的引用和拷贝问题

一.引用 a = ['a', 'b', 'c'] b = a print(id(a)) print(id(b)) 135300560 135300560 可以看到,变量a 和 b 的 id是完全一样的,这就说明a和b是同时指向内存的同一个区域的,即b随a的变化而变化. a = ['a', 'b', 'c'] b = a a[1] = 'd' print(b) b[2] = 'e' print(a) ['a', 'd', 'c'] ['a', 'd', 'e'] 二.浅拷贝 浅拷贝只是复制了的父对