定义

  • 递归是一种解决问题的方法,它把一个问题分解为越来越小的子问题,直到问题的规模小到可以被很简单直接解决。
  • 通常为了达到分解问题的效果,递归过程中要引入一个调用自身的函数。

举例

  • 数列求和
def listsum(numlist):
if len(numlist) == 1:
return numlist[0]
else:
return numlist[0]+listsum(numlist[1:]) if __name__ == "__main__":
print(listsum([1, 3, 5, 7, 9]))

递归三大定律

就像阿西莫夫(l.Asimov) 的“机器人三定律”一样,递归算法也要遵循三条重要的定律

  • 递归算法必须有个基本结束条件
  • 递归算法必须改变自己的状态并向基本结束条件演进
  • 递归算法必须递归地调用自身

应用

整数转换为任意进制

def toStr(n, base):
convertString = "0123456789ABCDEF"
if n < base:
return convertString[n] # 最小规模
else:
return toStr(n//base,base)+convertString[n % base] # 减小规模,调用自身 if __name__ == "__main__":
print(toStr(10,2))
print(toStr(255,16))

递归调用的实现

  • 当一个函数被调用的时候,系统会把调用时的现场数据压入到系统调用栈

    • 每次调用,压入栈的现场数据成为栈帧
    • 当函数返回时,要从调用栈的栈顶取得返回地址,恢复现场,弹出栈帧,按地址返回
  • 易碰到错误:RecursionError

    • 递归的层数太多,系统调用栈容量有限
    • 递归深度限制
      • 检查程序中是否忘记设置基本结束条件,导致无限递归
      • 或者向基本结束条件演进太慢,导致递归层数太多,调用栈溢出
    • 在python内置的sys模块可以获取和调整最大递归深度
    import sys
    sys.getrecursionlimit()#默认1000
    sys.setrecursionlimit(3000)#自定义设置
    sys.getrecursionlimit()

递归可视化

海归作图 turtle module

  • 爬行

    • forward(n)
    • backward(n)
  • 转向
    • left(a)
    • right(a)
  • 抬笔放笔
    • penup()
    • pendown()
  • 笔属性
    • pensize(s)
    • pencolor(c)

# 递归
def listsum(numlist):
if len(numlist) == 1:
return numlist[0]
else:
return numlist[0]+listsum(numlist[1:]) def toStr(n, base):
convertString = "0123456789ABCDEF"
if n < base:
return convertString[n]
else:
return toStr(n//base,base)+convertString[n % base] import turtle def draw_forward():
"""前向示例"""
t=turtle.Turtle()
# 作图开始
t.forward(100) #指挥海归作图
# 作图结束
turtle.done() def draw_square():
"""绘制正方形"""
t=turtle.Turtle()
for i in range (4):
t.forward(100)
t.right(90) turtle.done() def draw_pentagram():
"""绘制五角星"""
t=turtle.Turtle()
for i in range (5):
t.forward(100)
t.right(144) turtle.done() def drawSpiral(t,linelen):
"""绘制螺旋"""
if linelen>0:
t.forward(linelen)
t.right(90)
drawSpiral(t,linelen-10) if __name__ == "__main__":
#draw_forward()
#draw_square()
#draw_pentagram() t=turtle.Turtle()
drawSpiral(t,200)
turtle.done()

分形树:自相似递归图形

import turtle
def tree(t,branch_len):
if branch_len>5:
t.forward(branch_len)
t.right(20)
tree(t,branch_len-15)
t.left(40)
tree(t,branch_len-15)
t.right(20)
t.backward(branch_len) if __name__ == "__main__":
t=turtle.Turtle()
t.left(90)
t.penup()
t.backward(100)
t.pendown()
t.pencolor("green")
t.pensize(2)
tree(t,75)
t.hideturtle()
turtle.done()

谢尔宾斯基三角

作图思路

  • degree=n

    • 三角形是由3个degree=n-1的三角形按照品字形拼叠而成
    • 这个3个degree=n-1的三角形边长均为degree=n的三角形的一半(规模减小)
  • degree=0
    • 等边三角形,这是递归的基本结束条件

代码

import turtle
def sierpinski(t, degree, points):
"""谢尔宾斯基三角"""
colormap = ['blue', 'red', 'green', 'white', 'yellow', 'orange']
drawTriangle(t, points, colormap[degree])
if degree > 0:
# 左边三角形
sierpinski(t, degree-1,
{
'left': points['left'],
'top': getMid(points['left'], points['top']),
'right': getMid(points['left'], points['right'])
}
)
# 顶部三角形
sierpinski(t, degree-1,
{
'left': getMid(points['left'], points['top']),
'top': points['top'],
'right': getMid(points['top'], points['right'])
}
)
# 右边三角形
sierpinski(t, degree-1,
{
'left': getMid(points['left'], points['right']),
'top': getMid(points['top'], points['right']),
'right': points['right']
}
) def drawTriangle(t, 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) if __name__ == "__main__":
t = turtle.Turtle()
points = dict(
left=(-200, -100),
top=(0, 200),
right=(200, -100)
)
sierpinski(t, 5, points)
turtle.done()

汉诺塔

描述

  • 汉诺塔问题是法国数学家爱德华·卢卡斯于1883年发现的。他受到一个关于印度教寺庙的传说的启发,故事中这一问题交由年轻僧侣们解决。最开始,僧侣们得到三根杆子, 64个金圆盘堆叠在其中一根上, 每个圆盘比其下的小一点。僧侣们的任务是将64个圆盘从一根杆上转移到另一根杆上,但有两项重要的限制,一是他们一次只能移动一个圆盘,一是不能将大圆盘放在小圆盘之上。僧侣们日以继夜地工作,每秒移动一个圆盘。 传说中,当工作完成之时寺庙就会崩塌, 世界则将不复存在。
  • 传说很有趣,但也不用为世界末日将要到来而担心。毫无差错地完成这项工作需要264-1=18,446,774,073,709,551,615次移动。如果每秒移动一次则需要584,942,417,335(五千亿)年!显然实际上会更长

递归思路

  • 将盘片塔从开始柱,经由中间柱,移动到目标柱:

    1. 首先将上层N-1个盘片的盘片塔,从开始柱,经由目标柱,移动到中间柱
    2. 然后将第N个(最大)盘片,从开始柱,移动到目标柱
    3. 最后将放置在中间柱的N-1个盘片的盘片塔,经由开始柱,移动到目标柱
  • 基本结束条件,即最小规模问题:一个盘片的移动问题

代码实现

def moveTower(height,fromPole,withPole,toPole):
if height>=1:
moveTower(height-1,fromPole,toPole,withPole)
moveDisk(height,fromPole,toPole)
moveTower(height-1,withPole,fromPole,toPole) def moveDisk(disk,fromPole,toPole):
print(f"Moving disk[{disk}] from {fromPole} to {toPole}") if __name__ == "__main__":
moveTower(3,"#1","#2","#3") >>>
Moving disk[1] from #1 to #3
Moving disk[2] from #1 to #2
Moving disk[1] from #3 to #2
Moving disk[3] from #1 to #3
Moving disk[1] from #2 to #1
Moving disk[2] from #2 to #3
Moving disk[1] from #1 to #3

探索迷宫

原文

  • 古希腊迷宫

    • 克里特岛米诺斯王
    • 牛头人身怪物米诺陶洛斯
  • 圆明园 万花阵

算法思路

  • 步骤

    • 在初始位置尝试向北走一步,以此为开始递归程序。
    • 北面走不通的情况下则向南尝试,其后开始递归。
    • 南面走不通的情况下则向西尝试,其后开始递归。
    • 如果北面、南面、西面都走不通,则从东面开始并递归程序
    • 如果四个方向都走不出,则被困在迷宫中,以失败告终
  • 四种基本情况需要考虑:

    1、 海龟碰到“墙壁”,方格被占用无法通行。

    2、 海龟发现表示此方格已访问过,为避免陷入循环不在此位置继续寻找。

    3、 海龟碰到位于边缘的通道,即找到迷宫出口。

    4、 海龟在四个方向上探索都失败。

  • 递归归纳:

    • 海龟碰到“墙壁”方格,递归调用结束,返回失败
    • 海龟碰到“面包屑”方格,表示此方格已访问过,递归调用结束,返回失败
    • 海龟碰到“出口”方格,即“位于边缘的通道”方格,递归调用结束,返回成功!
    • 海龟在四个方向上的探索都失败,递归调用结束,返回失败

代码实现

  • 代码
import turtle
PART_OF_PATH = 'O'
TRIED = '.'
OBSTACLE = '+'
DEAD_END = '-'
class Maze:
def __init__(self):
self.mazelist = [
['+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+','+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+'],
['+', ' ', ' ', ' ', '+', ' ', ' ', ' ', '+', '+', ' ','+', '+', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', '+', ' ', ' ', ' ', ' ',' ', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+'],
['+', ' ', '+', ' ', ' ', ' ', ' ', '+', '+', ' ', ' ','+', '+', '+', '+', ' ', '+', '+', '+', ' ', '+', '+'],
['+', ' ', '+', ' ', ' ', ' ', '+', ' ', '+', ' ', '+','+', ' ', ' ', ' ', ' ', '+', '+', '+', ' ', ' ', '+'],
['+', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','+', '+', ' ', ' ', '+', '+', ' ', ' ', '+', ' ', '+'],
['+', '+', '+', '+', '+', ' ', '+', ' ', '+', ' ', ' ',' ', ' ', ' ', ' ', '+', '+', ' ', ' ', '+', ' ', '+'],
['+', '+', '+', '+', '+', ' ', '+', '+', '+', ' ', ' ','+', ' ', '+', ' ', ' ', '+', '+', ' ', ' ', ' ', '+'],
['+', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','+', ' ', '+', ' ', 'S', '+', ' ', '+', ' ', ' ', '+'],
['+', '+', '+', '+', '+', ' ', '+', ' ', ' ', '+', ' ','+', ' ', '+', ' ', ' ', ' ', ' ', ' ', '+', ' ', '+'],
['+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+','+', '+', '+', '+', '+', '+', '+', ' ', '+', '+', '+']
]
self.startRow=8
self.startCol=15
self.rowsInMaze =len(self.mazelist)
self.columnsInMaze =len(self.mazelist[0])
self.xTranslate=-self.columnsInMaze/2
self.yTranslate=self.rowsInMaze/2
self.t = turtle.Turtle()
self.t.shape('turtle')
self.wn = turtle.Screen()
self.wn.setworldcoordinates(-(self.columnsInMaze-1)/2-.5,
-(self.rowsInMaze-1)/2-.5,
(self.columnsInMaze-1)/2+.5,
(self.rowsInMaze-1)/2+.5) def drawMaze(self):
self.t.speed(10)
self.wn.tracer(0)
for y in range(self.rowsInMaze):
for x in range(self.columnsInMaze):
if self.mazelist[y][x] == OBSTACLE:
self.drawCenteredBox(x+self.xTranslate,-y+self.yTranslate,'orange')
self.t.color('black')
self.t.fillcolor('blue')
self.wn.update()
self.wn.tracer(1) def drawCenteredBox(self,x,y,color):
self.t.up()
self.t.goto(x-.5,y-.5)
self.t.color(color)
self.t.fillcolor(color)
self.t.setheading(90)
self.t.down()
self.t.begin_fill()
for i in range(4):
self.t.forward(1)
self.t.right(90)
self.t.end_fill() def moveTurtle(self,x,y):
self.t.up()
self.t.setheading(self.t.towards(x+self.xTranslate,-y+self.yTranslate))
self.t.goto(x+self.xTranslate,-y+self.yTranslate) def dropBreadcrumb(self,color):
self.t.dot(10,color) def updatePosition(self,row,col,val=None):
if val:
self.mazelist[row][col] = val
self.moveTurtle(col,row) if val == PART_OF_PATH:
color = 'green'
elif val == OBSTACLE:
color = 'red'
elif val == TRIED:
color = 'black'
elif val == DEAD_END:
color = 'red'
else:
color = None if color:
self.dropBreadcrumb(color) def isExit(self,row,col):
return (row == 0 or
row == self.rowsInMaze-1 or
col == 0 or
col == self.columnsInMaze-1 ) def __getitem__(self,idx):
return self.mazelist[idx] def searchFrom(maze, startRow, startColumn):
# 1.碰到墙壁,返回失败
maze.updatePosition(startRow, startColumn)
if maze[startRow][startColumn] == OBSTACLE :
return False # 2. 碰到面包屑,或者死胡同,返回失败
if maze[startRow][startColumn] == TRIED or maze[startRow][startColumn] == DEAD_END:
return False # 3. 碰到出口,返回成功
if maze.isExit(startRow,startColumn):
maze.updatePosition(startRow, startColumn, PART_OF_PATH)
return True # 4.洒下面包屑,继续探索
maze.updatePosition(startRow, startColumn, TRIED) # 向北南西东4个方向依次探索,or操作符具有短路效应
found = searchFrom(maze, startRow-1, startColumn) or \
searchFrom(maze, startRow+1, startColumn) or \
searchFrom(maze, startRow, startColumn-1) or \
searchFrom(maze, startRow, startColumn+1) # 如果探索成功,标记当前点,失败则标记为“死胡同”
if found:
maze.updatePosition(startRow, startColumn, PART_OF_PATH)
else:
maze.updatePosition(startRow, startColumn, DEAD_END)
return found if __name__ == "__main__":
myMaze = Maze()
myMaze.drawMaze()
myMaze.updatePosition(myMaze.startRow,myMaze.startCol)
searchFrom(myMaze, myMaze.startRow, myMaze.startCol)
turtle.done()

【数据结构与算法Python版学习笔记】递归(Recursion)——定义及应用:分形树、谢尔宾斯基三角、汉诺塔、迷宫的更多相关文章

  1. python 使用turtule绘制递归图形(螺旋、二叉树、谢尔宾斯基三角形)

    插图工具使用Python内置的turtle模块,为什么叫这个turtle乌龟这个名字呢,可以这样理解,创建一个乌龟,乌龟能前进.后退.左转.右转,乌龟的尾巴朝下,它移动时就会画一条线.并且为了增加乌龟 ...

  2. 【数据结构与算法Python版学习笔记】目录索引

    引言 算法分析 基本数据结构 概览 栈 stack 队列 Queue 双端队列 Deque 列表 List,链表实现 递归(Recursion) 定义及应用:分形树.谢尔宾斯基三角.汉诺塔.迷宫 优化 ...

  3. 【数据结构与算法Python版学习笔记】引言

    学习来源 北京大学-数据结构与算法Python版 目标 了解计算机科学.程序设计和问题解决的基本概念 计算机科学是对问题本身.问题的解决.以及问题求解过程中得出的解决方案的研究.面对一 个特定问题,计 ...

  4. 【数据结构与算法Python版学习笔记】树——相关术语、定义、实现方法

    概念 一种基本的"非线性"数据结构--树 根 枝 叶 广泛应用于计算机科学的多个领域 操作系统 图形学 数据库 计算机网络 特征 第一个属性是层次性,即树是按层级构建的,越笼统就越 ...

  5. 【数据结构与算法Python版学习笔记】递归(Recursion)——优化问题与策略

    分治策略:解决问题的典型策略,分而治之 将问题分为若干更小规模的部分 通过解决每一个小规模部分问题,并将结果汇总得到原问题的解 递归算法与分治策略 递归三定律 体现了分支策略 应用相当广泛 排序 查找 ...

  6. 【数据结构与算法Python版学习笔记】查找与排序——散列、散列函数、区块链

    散列 Hasing 前言 如果数据项之间是按照大小排好序的话,就可以利用二分查找来降低算法复杂度. 现在我们进一步来构造一个新的数据结构, 能使得查找算法的复杂度降到O(1), 这种概念称为" ...

  7. 【数据结构与算法Python版学习笔记】算法分析

    什么是算法分析 算法是问题解决的通用的分步的指令的聚合 算法分析主要就是从计算资源的消耗的角度来评判和比较算法. 计算资源指标 存储空间或内存 执行时间 影响算法运行时间的其他因素 分为最好.最差和平 ...

  8. 【数据结构与算法Python版学习笔记】基本数据结构——列表 List,链表实现

    无序表链表 定义 一种数据项按照相对位置存放的数据集 抽象数据类型无序列表 UnorderedList 方法 list() 创建一个新的空列表.它不需要参数,而返回一个空列表. add(item) 将 ...

  9. 【数据结构与算法Python版学习笔记】图——骑士周游问题 深度优先搜索

    骑士周游问题 概念 在一个国际象棋棋盘上, 一个棋子"马"(骑士) , 按照"马走日"的规则, 从一个格子出发, 要走遍所有棋盘格恰好一次.把一个这样的走棋序列 ...

随机推荐

  1. .NetCore 项目在服务器打包失败解决

    错误描述:NuGet警告 NU3037 NU3028 原因:Nuget无法访问到json所在的网络 2021年1月31日更新:更好的方法 把自动生成的Dockerfile内的AS build 替换成官 ...

  2. python实现遥感图像阈值分割

    1.阈值分割 import os import cv2 import numpy as np import matplotlib.pyplot as plt from osgeo import gda ...

  3. 痞子衡嵌入式:MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法. 前段时间痞子衡写了一篇 <在IAR开发环境下将关键函数重 ...

  4. 7-31 堆栈操作合法性 (20 分) PTA

    7-31 堆栈操作合法性 (20 分)   假设以S和X分别表示入栈和出栈操作.如果根据一个仅由S和X构成的序列,对一个空堆栈进行操作,相应操作均可行(如没有出现删除时栈空)且最后状态也是栈空,则称该 ...

  5. Intel® QAT 加速卡之IPSec示例

    Intel QAT 加速卡之IPSec示例 文章目录 Intel QAT 加速卡之IPSec示例 1. QAT处理IPSec入站报文 2. QAT处理IPSec出站报文 3. 组织架构 4. 示例源码 ...

  6. sed 找出含有某个字符串的行 注释掉

    1.源文件例子 [root@node1 ~]# cat /etc/fstab # # /etc/fstab # Created by anaconda on Mon Mar 1 18:32:15 20 ...

  7. 成本降低40%、资源利用率提高20%的 AI 应用产品云原生容器化之路

    作者 郭云龙,腾讯云高级工程师,目前就职于 CSIG 云产品三部-AI 应用产品中心,现负责中心后台业务框架开发. 导语 为了满足 AI 能力在公有云 SaaS 场景下,服务和模型需要快速迭代交付的需 ...

  8. P1013 [NOIP1998 提高组] 进制位

    解析 看到这道题时,有没有想到搜索?然后就是一通码......然后过了. 但是,真的要用搜索吗? 我们可以观察一下.对于n进制中的数ii,如果ii加上某一个数jj会变成两位数,那么可以得到如下不等式: ...

  9. JMeter主要元件

    配置元件 http cookie管理器 http信息头管理器 http请求默认值 统一管理 快速切换测试环境 http cache管理器 静态资源 监听器元件 查看结果树 分析查看某个请求的详情 请求 ...

  10. Linux下实现高可用软件-Keepalived基础知识梳理

    Keepalived介绍 Keepalived软件起初是专门为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP功能.因此,Keepali ...