参考自 MOOC数据结构与算法Python版
递归是一种解决问题的方法,它将问题分解为规模更小的相同问题,在算法流程中调用自身
问题:
给定一个不定长列表, 返回所有数的和。需要一个循环和一个累加变量来迭代求和。
举个例子:
求全括号表达式的和:
t o t a l = ( 1 + ( 3 + ( 5 + ( 7 + 9 ) ) ) ) total =(1+(3+(5+(7+9)))) total=(1+(3+(5+(7+9))))
最内层的括号(7+9), 这是无需循环即可计算的, 实际上整个求和的过程是这样:
t o t a l = ( 1 + ( 3 + ( 5 + 16 ) ) ) total = (1+(3+(5+16))) total=(1+(3+(5+16)))
t o t a l = ( 1 + ( 3 + 21 ) ) total = (1+(3+21)) total=(1+(3+21))
t o t a l = ( 1 + 24 ) total = (1+24) total=(1+24)
t o t a l = 25 total =25 total=25
def listsum(numList):
if len(numList) == 1:
return numList[0]
else:
return numList[0] + listsum(numList[1:])
print(listsum([1,3,4,2,7]))
def toStr(n,base):
convertString = "0123456789ABCDEF"
if n<base:
return convertString[n]
else:
return toStr(n//base,base) + convertString[n%base]
print (toStr(1453,16))
Python中的递归深度限制
import sys
print(sys.getrecursionlimit()) #1000
sys.setrecursionlimit(3000)
print(sys.getrecursionlimit()) #3000
推荐两部关于闭环递归的电影: 《predestination》–前目的地,《Triangle》–恐怖邮轮
下面我们通过递归作图来展现递归调用的视觉影像。
Python内置,随时可用,以LOGO语言的创意为基础
其意象为模拟海龟在沙滩上爬行而留下的足迹
– 爬行: forward(n); backward(n)
– 转向: left(a); right(a)
– 抬笔放笔: penup(); pendown()
– 笔属性: pensize(s); pencolor©
PS:用这个画图,系统容易崩
画一个五角星:
import turtle
t = turtle.Turtle()
t.pencolor('red')
t.pensize(3)
for i in range(5):
t.forward(100)
t.right(144)
t.hideturtle() #隐藏画笔的turtle形状
turtle.done()
import turtle
t = turtle.Turtle()
def drawSpiral(t, lineLen):
if lineLen>0:
t.forward(lineLen)
t.right(90)
drawSpiral(t,lineLen-5)
drawSpiral(t,100)
turtle.done()
问题: 假设你为一家自动售货机厂家编程序,自动售货机要每次找给顾客最少数量硬币;
解决方案:
肯定能找到最优解的方法 – 贪心策略
(1). 确定基本结束条件
需要兑换的找零,其面值正好等于某种硬币
(2). 减少问题的规模,对每种硬币尝试1次, 例如美元硬币体系:
找零减去1分(penny)后,求兑换硬币最少数量(递归调用自身);
找零减去5分(nikel)后,求兑换硬币最少数量
找零减去10分(dime)后,求兑换硬币最少数量
找零减去25分(quarter)后,求兑换硬币最少数量
上述4项中选择最小的一个。
n u m C o i n s = m i n { 1 + n u m C o i n s ( o r i g i n a l a m o u t − 1 ) 1 + n u m C o i n s ( o r i g i n a l a m o u t − 5 ) 1 + n u m C o i n s ( o r i g i n a l a m o u t − 10 ) 1 + n u m C o i n s ( o r i g i n a l a m o u t − 25 ) numCoins=min\left\{ \begin{matrix} \begin{matrix} \begin{matrix} 1+numCoins(originalamout-1) \\ 1+numCoins(originalamout-5) \\ \end{matrix} \\ 1+numCoins(originalamout-10) \\ \end{matrix} \\ 1+numCoins(originalamout-25) \\ \end{matrix} \right. numCoins=min⎩⎪⎪⎨⎪⎪⎧1+numCoins(originalamout−1)1+numCoins(originalamout−5)1+numCoins(originalamout−10)1+numCoins(originalamout−25)
[注]:1表示其面值正好等于某种硬币,因此只需要1个硬币
(3). 递归解法代码:
## 虽然能解决问题,但极其低效
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))
def recDC(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 + recDC(coinValueList, change-i, knownResults)
if numCoins < minCoins:
minCoins = numCoins
knownResults[change] = minCoins #找到最优解,记录在表中
return minCoins
print (recDC([1, 5, 10, 25], 63, [0]*64))
def dpMakeChange(coinValueList, change, minCoins):
for cents in range(1, change+1): #从1开始到change逐个计算最少硬币书
coinCount = cents#初始化一个最大值
#减去每个硬币,向后查最少硬币数,同时记录总的最少数
for j in [c for c in coinValueList if c <= cents]:
if minCoins[cents - j] + 1 < coinCount:
coinCount = minCoins[cents -j] + 1
minCoins[cents] = coinCount #得到当前最少硬币数,记录到表中
return minCoins[change]
print(dpMakeChange([1, 5, 10, 21, 25], 63, [0]*64))
def dpMakeChange(coinValueList, change, minCoins,coinsUsed):
for cents in range(1, 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 #记录本步骤加的1个硬币
return minCoins[change]
def printCoins(coinsUsed, change):
coin = change
while coin > 0:
thisCoin = coinsUsed[coin]
print(thisCoin)
coin = coin - thisCoin
amnt = 63
clist = [1, 5, 10, 21, 25]
coinsUsed = [0] * (amnt + 1)
coinsCount= [0] * (amnt + 1)
print("Making change for ", amnt, "requires")
print(dpMakeChange(clist, amnt, coinsCount,coinsUsed), "coins")
print("They are:")
printCoins(coinsUsed, amnt)
print("Th used list is as follows:")
print(coinsUsed)