此章理解较为困难,未写自写代码,仅仅记录标准代码并复现。
-
什么是递归(Recursion)
递归是一种解决问题的方法,其精髓在于将问题分解为规模更小的相同问题。持续分解,直到问题规模小到可以用非常简单直接的方式来解决。递归的问题分解方法非常独特,其算法方面的明显特征就是:在算法流程中调用自身。 -
递归的三定律:
1.递归算法必须有一个基本结束条件(最小规模问题的直接解决)
2.递归算法必须能改变状态向基本结束条件演进(减小问题规模)
3.递归算法必须调用自身(解决减小了规模的相同问题) -
初识递归:数列求和
算法思路:数列的和 = 首个数 + 余下数列的和 ,如果数列包含的数少到只有1个的话,它的和就是这个数了。
代码实现:
def ListSum(alist):
if len(alist) == 1:
return alist[0]
else:
return alist[0] + ListSum(alist[1 : ])
print(ListSum([1,3,5,7,9]))
根据递归的“三定律”来分析下数列求和问题:
1.数列求和问题首先具备了基本结束条件:当列表长度为1的时候,直接输出所包含的唯一数。
2.数列求和处理的数据对象是一个列表,而基本结束条件是长度为1的列表,那递归算法就要改变列表并向长度为1的状态演进。(具体做法为将列表长度减1)
3.调用自身:在数列求和算法中就是”更短数列的求和问题“
- 递归版本的任意数制转换:
算法思路:
1.假设是要转换为十进制,比十小的整数转换为十进制,直接查表就可以得到,比十大的整数就想办法拆成一系列比十小的整数,逐个查表。
2.所以递归基本结束条件就是小于十的整数,拆解整数的过程就是向基本结束条件演进的过程。
3.拆解过程分为两步:1. 除以“进制基base” (// base);2. 对“进制基”求余数(% base)。
4.此时问题就分解为:1. 余数总小于“进制基base”,是基本结束条件,可直接查表转换;2. 整数商成为更小规模问题,通过递归调用自身解决。
代码实现:
def toStr(n,base):
constring = '0123456789ABCDEF'
if n < base:
return constring[n]
else:
return toStr(n//base,base) + constring(n % base)
注意点为 return toStr(n//base,base) + constring(n % base)此语句,因为取余数之后要”倒“写结果(最先得到的余数放在最后面)所以根据递归过程,要把进入递归的”入口“写在得到余数的前面。
- 递归调用的实现:
在python中递归深度有限制:
def tell_story():
print("从前有座山,山上有座庙,庙里有个老和尚,他在讲:”)
tell_story()
print("给你讲个故事")
tell_story()
这时候要检查程序中是否忘记设计基本结束条件,导致无限递归。或者向基本技术条件的演进太慢,导致递归层数太多,调用栈溢出。
-
python中的递归深度限制:
-
递归的可视化:图示
代码实现:
import turtle
# 正方形:
t = turtle.Turtle()
for i in range(4):
t.towards(100)
t.right(90)
turtle.done()
# 五角星:
t = turtle.Turtle()
t.pencolor("red")
t.pensize(3)
for i in range(5):
t.towards(100)
t.right(144)
t.hideturtle()
turtle.done()
# 螺旋线:
t = turtle.Turtle()
def drawSpiral(t,lineLen):
if lineLen > 0:
t.forward(lineLen)
t.right(90)
drawSpiral(t,lineLen-5)
drawSpiral(t,200)
turtle.done()
- 分形树:自相似递归图形问题:
分形Fractal,是1975年由Mandelbrot开创的新学科。“一个粗糙或零碎的几何形状,可以分成数个部分,且每一部分都(至少近似地)是整体缩小后的形状”,即具有自相似的性质。
分形是在不同尺度上都具有相似性的事物。
代码:
# 分形树:
def tree(branch_len):
if branch_len > 5: # 树干太短不画,即递归结束条件
t.forward(branch_len) # 画树干
t.right(20) # 向右倾斜20度
tree(branch_len - 15) # 递归调用,画右边的小树,树干减15
t.left(40) # 向左回40度,即左倾斜20度
tree(branch_len - 15) # 递归调用,画左边的小树,树干减15
t.right(20) # 向右回20度,即回正
t.backward(branch_len) # 海龟退回原位置
t = turtle.Turtle()
t.left(90)
t.penup()
t.backward(100)
t.pendown()
t.pencolor("green")
t.pensize(2)
tree(75) # 画树干长度75的二叉树
t.hideturtle()
turtle.done()
- 谢尔宾斯基Sierpinski三角形:
分形构造,平面称谢尔宾斯基三角形,立体称谢尔宾斯基金字塔。实际上,真正的谢尔宾斯基三角形是完全不可见的,其面积为0,但周长无穷,是介于一维和二维之间的分数维(约1.585维)构造。
根据自相似特性,谢尔宾斯基三角形是由3个尺寸减半的谢尔宾斯基三角形按照品字形拼叠而成。由于我们无法真正做出谢尔宾斯基三角形(degree —> ∞),只能做degree有限的近似图形。
在degree有限的情况下,degree=n的三角形,是由3个degree=n-1的三角形按照品字形拼叠而成。同时,这3个degree=n-1的三角形边长均为degree=n的三角形的一半(规模减小)。当degree=0,则就是一个等边三角形,这是递归基本结束条件
代码参考一个更好理解的代码:
#!/usr/bin/python3
import turtle
t = turtle.Turtle()
def get_midpoint(a, b):
ax, ay = a
bx, by = b
return (ax + bx) / 2, (ay + by) / 2
def draw_triangle(a, b, c):
ax, ay = a
bx, by = b
cx, cy = c
t.penup()
t.goto(ax, ay)
t.pendown()
t.goto(bx, by)
t.goto(cx, cy)
t.goto(ax, ay)
t.penup()
def draw_sierpinski(triangle, depth):
"""
:param triangle: 指定三角形三个顶点坐标,示例:((ax,ay),(bx,by),(cx,cy))。
:param depth: 指定层数
"""
a, b, c = triangle
draw_triangle(a, b, c)
if depth == 0:
return
else:
d = get_midpoint(a, b)
e = get_midpoint(b, c)
f = get_midpoint(c, a)
draw_sierpinski([a, d, f], depth-1)
draw_sierpinski([d, b, e], depth-1)
draw_sierpinski([f, e, c], depth-1)
if __name__ == '__main__':
triangle = [[-200, -100], [0, 200], [200, -100]]
draw_sierpinski(triangle, 5)
turtle.done()
原课程代码为:
import turtle
def sierpinski(degree,points):
colormap = ['blue','red','green','white','yellow','orange']
drawTriangle(points,colormap[degree])
if degree>0:
sierpinski(degree-1,
{'left':points['left'],
'right':getMid(points['left'],points['right']),
'top':getMid(points['top'],points['left'])})
sierpinski(degree-1,
{'left':getMid(points['top'],points['left']),
'right':getMid(points['left'],points['right']),
'top':points['top']})
sierpinski(degree - 1,
{'left': getMid(points['top'], points['left']),
'right': points['right'],
'top': getMid(points['top'], points['right'])})
def drawTriangle(points,color):
t.fillcolor(color)
t.penup()
t.goto(points['top'])
t.pendown()
t.begin_fill()
t.goto(points['left'])
t.goto(points['right'])
t.goto(points['top'])
t.end_fill()
def getMid(p1,p2):
return ((p1[0]+p2[0])/2,(p1[1]+p2[1])/2)
t = turtle.Turtle()
points = {'left':(-200,-100),
'top':(0,200),
'right':(200,-100)}
sierpinski(5,points)
turtle.done()
- 汉诺塔问题:
def moveTower(height,fromPole,withPole,toPole):
if height>=1:
moveTower(height-1,fromPole,toPole,withPole)
moveDisk(fromPole,toPole)
moveTower(height-1,withPole,fromPole,toPole)
def moveDisk(disk,fromPole,toPole):
print(f"moving disk[{disk}] from {fromPole} to {toPole}")
moveTower(3,"#1","#2","#3")
参考博文:
https://blog.csdn.net/python1639er/article/details/104038495?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159471622819725219913743%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=159471622819725219913743&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v3~pc_rank_v2-4-104038495.first_rank_ecpm_v3_pc_rank_v2&utm_term=python%E8%B0%A2%E5%B0%94%E5%AE%BE%E6%96%AF%E5%9F%BA