Python数据结构(四)——递归

递归

递归是一种解决问题的方法,将问题分解为更小的子问题,直到得到一个足够小的问题可以被很简单的解决。通常递归涉及函数调用自身。递归允许我们编写优雅的解决方案,解决可能很难编程的问题。

计算整数列表和

# version1
def list_sum(num_list):
    num_sum = 0
    for i in num_list:
        num_sum += i
    return num_sum
print list_sum([1,3,5,7,9])
25
# version2:不使用循环
def list_num2(num_list):
    if len(num_list)==1:
        return num_list[0]
    else:
        return num_list[0]+list_num2(num_list[1:])
print list_num2([1,3,5,7,9])

25

递归三定律

  • 递归算法必须具有基本情况
  • 递归算法必须改变其状态并向基本情况靠近。
  • 递归算法必须以递归方式调用自身。

整数转任意进制字符

def to_str(n,base):
    conver_string = "0123456789ABCDEF"
    if n < base:
        return conver_string[n]
    else:
        return str(to_str(n//base,base))+conver_string[n%base]
print to_str(1453,16)
5AD

栈帧:实现递归

from pythonds.basic.stack import Stack
rStack = Stack()

def to_str(n,base):
    conver_string = "0123456789ABCDEF"
    while n>0:
        if n<base:
            rStack.push(conver_string[n])
        else:
            rStack.push(conver_string[n%base])
        n //= base
    res = ""
    while not rStack.isEmpty():
        res += str(rStack.pop())
    return res

print to_str(1453,16)
5AD

可视化递归

# turtle 是 Python 所有版本的标准库,插图的工具
import turtle
myTurtle = turtle.Turtle()
myWin = turtle.Screen()

def drawSpiral(myTurtle,linelen):
    if linelen > 0:
        # 直走
        myTurtle.forward(linelen)
        # 右拐
        myTurtle.right(90)
        # 递归
        drawSpiral(myTurtle,linelen)
drawSpiral(myTurtle,100)
# 调用函数 myWin.exitonclick(),这是一个方便的缩小窗口的方法,
# 使乌龟进入等待模式,直到你单击窗口,然后程序清理并退出。
myWin.exitonclick()

# 绘制分型树
import turtle
def tree(branchlen,t):
    if branchlen > 5:
        t.forward(branchlen)
        t.right(20)
        tree(branchlen-15,t)
        t.left(40)
        tree(branchlen-10,t)
        t.right(20)
        t.backward(branchlen)
def main():
    t = turtle.Turtle()
    myWin = turtle.Screen()
    t.left(90)
    t.up()
    t.backward(100)
    t.down()
    t.color("green")
    tree(75,t)
    myWin.exitonclick()
main()

谢尔宾斯基三角形

import turtle

def drawTriangle(points,color,myTurtle):
    myTurtle.fillcolor(color)
    myTurtle.up()
    myTurtle.goto(points[0][0],points[0][1])
    myTurtle.down()
    myTurtle.begin_fill()
    myTurtle.goto(points[1][0],points[1][1])
    myTurtle.goto(points[2][0],points[2][1])
    myTurtle.goto(points[0][0],points[0][1])
    myTurtle.end_fill()

def getMid(p1,p2):
    return ( (p1[0]+p2[0]) / 2, (p1[1] + p2[1]) / 2)

def sierpinski(points,degree,myTurtle):
    colormap = ['blue','red','green','white','yellow',
                'violet','orange']
    drawTriangle(points,colormap[degree],myTurtle)
    if degree > 0:
        sierpinski([points[0],
                        getMid(points[0], points[1]),
                        getMid(points[0], points[2])],
                   degree-1, myTurtle)
        sierpinski([points[1],
                        getMid(points[0], points[1]),
                        getMid(points[1], points[2])],
                   degree-1, myTurtle)
        sierpinski([points[2],
                        getMid(points[2], points[1]),
                        getMid(points[0], points[2])],
                   degree-1, myTurtle)

def main():
   myTurtle = turtle.Turtle()
   myWin = turtle.Screen()
   myPoints = [[-100,-50],[0,100],[100,-50]]
   sierpinski(myPoints,3,myTurtle)
   myWin.exitonclick()

main()

汉诺塔游戏

有一个印度教寺庙,将谜题交给年轻的牧师。在开始的时候,牧师们被给予三根杆和一堆 64 个金碟,每个盘比它下面一个小一点。他们的任务是将所有 64 个盘子从三个杆中一个转移到另一个。有两个重要的约束,它们一次只能移动一个盘子,并且它们不能在较小的盘子顶部上放置更大的盘子。牧师日夜不停每秒钟移动一块盘子。当他们完成工作时,传说,寺庙会变成灰尘,世界将消失。

虽然传说是有趣的,你不必担心世界不久的将来会消失。移动 64 个盘子的塔所需的步骤数是 2^64 -1 = 18,446,744,073,709,551,615264-1 = 18,446,744,073,709,551,615。以每秒一次的速度,即584,942,417,355584,942,417,355 年!。

这里是如何使用中间杆将塔从起始杆移动到目标杆的步骤:

  • 使用目标杆将 height-1 的塔移动到中间杆。
  • 将剩余的盘子移动到目标杆。
  • 使用起始杆将 height-1 的塔从中间杆移动到目标杆。
# 一开始盘子都在起始杆上
# Python 提供了我们需要调用的隐含的栈。
def moveTower(height,fromPole, toPole, withPole):
    if height >= 1:
        moveTower(height-1,fromPole,withPole,toPole)
        moveDisk(fromPole,toPole)
        moveTower(height-1,withPole,toPole,fromPole)
def moveDisk(fp,tp):
    print("moving disk from",fp,"to",tp)
moveTower(4,'fromPole', 'toPole', 'withPole')
('moving disk from', 'fromPole', 'to', 'withPole')
('moving disk from', 'fromPole', 'to', 'toPole')
('moving disk from', 'withPole', 'to', 'toPole')
('moving disk from', 'fromPole', 'to', 'withPole')
('moving disk from', 'toPole', 'to', 'fromPole')
('moving disk from', 'toPole', 'to', 'withPole')
('moving disk from', 'fromPole', 'to', 'withPole')
('moving disk from', 'fromPole', 'to', 'toPole')
('moving disk from', 'withPole', 'to', 'toPole')
('moving disk from', 'withPole', 'to', 'fromPole')
('moving disk from', 'toPole', 'to', 'fromPole')
('moving disk from', 'withPole', 'to', 'toPole')
('moving disk from', 'fromPole', 'to', 'withPole')
('moving disk from', 'fromPole', 'to', 'toPole')
('moving disk from', 'withPole', 'to', 'toPole')

动态规划

假设你是一个自动售货机制造商的程序员。你的公司希望通过给每个交易最少硬币来简化工作。假设客户放入 1 美元的钞票并购买 37 美分的商品。你可以用来找零的最小数量的硬币是多少?

假设你的公司决定在埃尔博尼亚部署自动贩卖机,除了通常的 1,5,10 和 25 分硬币,他们还有一个 21 分硬币 。

def recMC(coinValueList,change):
    minCoins = change
    if change in coinValueList:
        return 1
    else:
        for i in [c for c in coinValueList if c <= change]:
            numCoins = 1 + recMC(coinValueList,change-i)
            if numCoins < minCoins:
                minCoins = numCoins
    return minCoins
print recMC([1,5,10,25],63)
6

这种算法是非常低效的。事实上,它需要 67,716,925 个递归调用来找到 4 个硬币的最佳解决 63 美分问题的方案。

减少我们工作量的关键是记住一些过去的结果,这样我们可以避免重新计算我们已经知道的结果。一个简单的解决方案是将最小数量的硬币的结果存储在表中。

def recMC(coinValueList,change,knownResults):
    minCoins = change
    if change in coinValueList:
        knownResults[change] = 1
        return 1
    elif knownResults[change] > 0:
        return knownResults[change]
    else:
        for i in [c for c in coinValueList if c <= change]:
            numCoins = 1 + recMC(coinValueList,change-i,knownResults)
            if numCoins < minCoins:
                minCoins = numCoins
                knownResults[change] = minCoins
    return minCoins
print recMC([1,5,10,25],63,[0]*64)
6

这个修改的算法减少了我们需要为四个硬币递归调用的数量,63美分问题只需 221 次调用!

使用动态规划算法:

def dpMakeChange(coinValueList,change,minCoins):
    for cents in range(change+1):
        coinCount = cents
        for j in [c for c in coinValueList if c<= cents]:
            if minCoins[cents-j]+1<coinCount:
                coinCounts = minCoins[cents-j]+1
        minCoins[cents] = coinCount
    return minCoins[change]
# 跟踪使用的硬币
def dpMakeChange(coinValueList,change,minCoins,coinsUsed):
    for cents in range(change+1):
        coinCount = cents
        newCoin = 1
        for j in [c for c in coinValueList if c <= cents]:
            if minCoins[cents-j] + 1 < coinCount:
                coinCount = minCoins[cents-j]+1
                newCoin = j
        minCoins[cents] = coinCount
        coinsUsed[cents] = newCoin
    return minCoins[change]

def printCoins(coinsUsed,change):
    coin = change
    while coin > 0:
        thisCoin = coinsUsed[coin]
        print thisCoin
        coin = coin - thisCoin

def main():
    amnt = 63
    clist = [1,5,10,21,25]
    coinsUsed = [0]*(amnt+1)
    coinCount = [0]*(amnt+1)

    print("Making change for",amnt,"requires")
    print(dpMakeChange(clist,amnt,coinCount,coinsUsed),"coins")
    print("They are:")
    printCoins(coinsUsed,amnt)
    print("The used list is as follows:")
    print(coinsUsed)
main()
('Making change for', 63, 'requires')
(3, 'coins')
They are:
21
21
21
The used list is as follows:
[1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 10, 1, 1, 1, 1, 5, 1, 1, 1, 1, 10, 21, 1, 1, 1, 25, 1, 1, 1, 1, 5, 10, 1, 1, 1, 10, 1, 1, 1, 1, 5, 10, 21, 1, 1, 10, 21, 1, 1, 1, 25, 1, 10, 1, 1, 5, 10, 1, 1, 1, 10, 1, 10, 21]
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值