递归
递归算法是一种直接或者间接调用自身函数或者方法的算法。
递归算法的实质是把问题分解为规模更小的同类子问题,然后递归调用方法来表示问题的解。
- 该问题的解可以分解为几个子问题的解;
- 这个问题与分解后的子问题,除了数据规模的不同,求解思路完全一样;
- 存在递归终止条件,即,必须有一个明确的递归结束条件,称之为递归出口。
数组求和
对 1 到 n 这 n 个数字进行求和,可以看做是 n 加上 1 到 n-1 这 n-1 个数字求和的结果;并以此逐步分解,直到最后是 0 与 1 进行求和。
def sum_func(n):
if n == 1:
return 1
else:
return n + sum_func(n-1)
汉诺塔问题
汉诺塔是典型的可以用 分治算法 解决的问题,以下内容是采用分治思想的递归算法。
问题描述:
有三个盘座A、B、C,A上有64个盘子,盘子大小不等,大在下,小在上。若要把盘子从A移到B,每次只能移动一个,且移动过程中,3个座上始终保持:大在上,小在下。
问题分析:
- 如果只有一个盘子,则不需要借助C,直接从A移动到B。
- 如果只有两个盘子,则可以先把A的最上面一个盘子2移动到C;再将盘子1移动到B;然后将盘子2移动到B。这说明了借助C可以将2个盘子从A移动到B;同样,也说明借助B可以将2个盘子从A移动到C。
- 如果有3个盘子,那么根据2个盘子的结论,可以借助B将盘子1上的两个盘子(盘子2和盘子3)从A移动到C;将盘子1从A移动到B,A则变为空座;借助A座,将C上的两个盘子移动到B。
- 以此类推,上述思路可以扩展到 n 个盘子的情况,将较小的 n-1 个盘子看做一个整体,也就是我们要求的子问题,初始A上有 n 个盘子,B、C上为空;可以借助B,将A上面的 n-1 个盘子从A移动到C;将A上最大的盘子1移动到B,A为空座;可以借助B,将C上的 n-2 个盘子从C移动到A;将C上最大的盘子2(从整体看,为第二大)移动到B,C为空座;…
代码示例:
def move(n, source, target):
print('The', n, 'th', 'plate move: ', source, '------>', target)
def hanoi(n, source, temp, target):
if n == 1:
# only one plate, move it directly from source to target
move(n, source, target)
else:
# move the top n-1 plates from source to temp through target
hanoi(n-1, source, target, temp)
# move the n plate(the largest and lowest plate) from source to target
move(n, source, target)
# move the top n-1 plates from temp to target through source
hanoi(n-1, temp, source, target)
hanoi(3, 'A', 'C', 'B')
计算阶乘
# 实现阶乘
def factorial_z(n):
if n == 1:
return n
return n*factorial_z(n-1)
计算组合数
# 求组合数
def combination_z(n, m):
if m == n:
return 1
elif m == 1:
return n
else:
return combination_z(n-1, m-1) + combination_z(n-1, m)
在不使用标准库的情况下,为 C k n C_k^{n} Ckn 编写一个函数。
def factorial(n):
if n==0:
return 1
return (n * factorial(n-1) )
def nCr(n,k):
return factorial(n)/(factorial(k) * factorial(n-k))
对于大的数字,存在更好的解决方案,没有必要三次调用阶乘函数。
此外,如果分子和分母都很大,很可能会发生数字溢出。
def nCr_norecur(n,k):
r=1
for i in range(k):
r *= n-i
r = r / (i+1)
return r
Coding Challenge
Fizz
给你一个整数比如 100,需要输出一个列表。列表中的每个元素,如果能被 3 整除则输出 ‘Fizz’。
def fizz(n):
res = []
for i in range(1, n + 1):
if i % 3 == 0:
res.append('Fizz')
else:
res.append(str(i))
return res
FizzBuzz
给你一个整数比如 100,需要输出一个列表。列表中的每个元素,如果能被 3 整除则输出 ‘Fizz’,如果被 5 整除则输出 ‘Buzz’,如果能同时被 3 和 5 整除,则输出 ‘FizzBuzz’。
def fizzbuzz(n):
res = []
for i in range(1, n + 1):
if i % 3 == 0 and i % 5 == 0:
res.append('FizzBuzz')
elif i % 3 == 0 and i % 5 != 0:
res.append('Fizz')
elif i % 3 != 0 and i % 5 == 0:
res.append('Buzz')
else:
res.append(str(i))
return res
FizzBuzzPrime
在 FizzBuzz 的基础上,识别质数。如果是质数,则输出为 “Prime”。所以代码中我们需要新增一个判断是否为质数的 Function。
def is_prime(n):
if n == 1:
return False
else:
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
else:
return True
def fizzbuzzprime(n):
res = []
for i in range(1, n + 1):
if is_prime(i):
res.append('Prime')
elif i % 3 == 0 and i % 5 == 0:
res.append('FizzBuzz')
elif i % 3 == 0:
res.append('Fizz')
elif i % 5 == 0:
res.append('Buzz')
else:
res.append(str(i))
return res
FizzBuzzPrimePi
在 FizzBuzzPrime 的基础上, 如果数字是 ‘Pi’ 前 n 位字符串的一个连续子集,则需要输出 ‘Pi’。
题目会给定 n 和 p 两个数字,比如 n=15,p=3。那么答案是 [‘Pi’, ‘Prime’, ‘Pi’, ‘Pi’, ‘Buzz’, ‘Fizz’, ‘Prime’, ‘8’, ‘Fizz’, ‘Buzz’, ‘Prime’, ‘Fizz’, ‘Prime’, ‘Pi’, ‘FizzBuzz’]。
题目中的 1, 3, 4, 14都输出为 “Pi”,是因为题目给定 p=3,也就是说只要是 314 的连续子集,则对应位置的数字要输出为 ‘Pi’。(如果 p=4,则只要是 3141 的连续子集,则对应位置的数字要输出为 ‘Pi’)。
- 问题就在于怎么样计算 pi,因为 math 模块中的 pi 只精确到小数点后 16 位,当题目中 p 大于 17 时就无法使用 python 内置的 pi。
- 考虑 n 和 p 之间的关系,是否存在一个值,当 p 大于这个值时,所有 n 的输出都是 pi?