递归与格式化:编程中的重要概念
递归的概念与应用
递归是一种以自身来定义对象或过程的方法。就像一个孩子睡不着,妈妈给她讲一只小青蛙睡不着的故事,小青蛙的妈妈又给它讲小熊睡不着的故事,小熊的妈妈再给它讲小鼬鼠的故事,直到小鼬鼠睡着,然后小熊、小青蛙、孩子依次睡着。
递归在很多方面都有应用:
-
数学模式
:阶乘和斐波那契数列等数学模式是递归的。例如阶乘,(n!) 当 (n = 0) 时为 (1),当 (n > 0) 时为 (n\times(n - 1)!)。
-
文档结构
:网页等文档具有自然的递归性。一个网页可以包含在一个盒子中,这个盒子又可以包含另一个网页,如此递归下去。
-
分形图像
:分形图像的工作原理也是递归的。
递归函数常用于高级搜索和排序算法中。即使不是程序员,理解递归系统的概念也很重要。比如在设计食谱的网页程序时,熟悉递归的人会考虑到每个食材本身也可以是包含其他食材的食谱,这样的系统会更强大。
递归的编码实现
在编程中,函数可以调用其他函数,也可以调用自身,这就是递归。例如:
def f():
print("Hello")
f()
f()
这个例子会不断打印 “Hello” 并调用
f()
函数,直到计算机耗尽栈空间,Python 会输出错误 “RuntimeError: maximum recursion depth exceeded”。
为了控制递归深度,需要有方法防止函数无限调用自身。以下是一个控制递归深度的例子:
def f(level):
print("Recursion call, level", level)
if level < 10:
f(level + 1)
f(1)
这个函数会打印递归调用的层级,当达到 10 层时停止调用。
递归阶乘计算
计算阶乘是递归的经典应用。以下是使用递归和非递归方法计算阶乘的代码:
# 非递归方法计算阶乘
def factorial_nonrecursive(n):
answer = 1
for i in range(2, n + 1):
print(i, "*", answer, "=", i * answer)
answer = answer * i
return answer
print("I can calculate a factorial!")
user_input = input("Enter a number:")
n = int(user_input)
answer = factorial_nonrecursive(n)
print(answer)
# 递归方法计算阶乘
def factorial_recursive(n):
if n == 1:
return n
else:
x = factorial_recursive(n - 1)
print(n, "*", x, "=", n * x)
return n * x
print("I can calculate a factorial!")
user_input = input("Enter a number:")
n = int(user_input)
answer = factorial_recursive(n)
print(answer)
递归矩形绘制
在
pygame
程序中,也可以看到递归的应用。以下是一个递归绘制矩形的例子:
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
def recursive_draw(x, y, width, height):
pygame.draw.rect(screen, BLACK, [x, y, width, height], 1)
if width > 14:
x += width * .1
y += height * .1
width *= .8
height *= .8
recursive_draw(x, y, width, height)
pygame.init()
size = [700, 500]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(WHITE)
recursive_draw(0, 0, 700, 500)
pygame.display.flip()
clock.tick(60)
pygame.quit()
这个程序会递归地在一个大矩形内绘制逐渐变小的矩形,每个矩形比其父矩形小 20%。
递归分形绘制
分形也可以通过递归实现。以下是一个简单分形的绘制代码:
import pygame
black = (0, 0, 0)
white = (255, 255, 255)
green = (0, 255, 0)
red = (255, 0, 0)
def recursive_draw(x, y, width, height, count):
pygame.draw.line(screen, black, [x + width*.25, height // 2 + y], [x + width*.75, height // 2 + y], 3)
pygame.draw.line(screen, black, [x + width * .25, (height * .5) // 2 + y], [x + width * .25, (height * 1.5) // 2 + y], 3)
pygame.draw.line(screen, black, [x + width * .75, (height * .5) // 2 + y], [x + width * .75, (height * 1.5) // 2 + y], 3)
if count > 0:
count -= 1
recursive_draw(x, y, width // 2, height // 2, count)
recursive_draw(x + width // 2, y, width // 2, height // 2, count)
recursive_draw(x, y + width // 2, width // 2, height // 2, count)
recursive_draw(x + width // 2, y + width // 2, width // 2, height // 2, count)
pygame.init()
size = [700, 700]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(white)
fractal_level = 3
recursive_draw(0, 0, 700, 700, fractal_level)
pygame.display.flip()
clock.tick(20)
pygame.quit()
递归二分查找
递归也可以用于二分查找。以下是非递归和递归的二分查找代码:
# 非递归二分查找
def binary_search_nonrecursive(search_list, key):
lower_bound = 0
upper_bound = len(search_list) - 1
found = False
while lower_bound < upper_bound and found == False:
middle_pos = (lower_bound + upper_bound) // 2
if search_list[middle_pos] < key:
lower_bound = middle_pos + 1
elif search_list[middle_pos] > key:
upper_bound = middle_pos
else:
found = True
if found:
print("The name is at position", middle_pos)
else:
print("The name was not in the list.")
# 递归二分查找
def binary_search_recursive(search_list, key, lower_bound, upper_bound):
middle_pos = (lower_bound + upper_bound) // 2
if search_list[middle_pos] < key:
binary_search_recursive(search_list, key, middle_pos + 1, upper_bound)
elif search_list[middle_pos] > key:
binary_search_recursive(search_list, key, lower_bound, middle_pos)
else:
print("Found at position", middle_pos)
格式化的基本概念
在编程中,格式化可以让输出的文本更加美观和易读。以下是一个文本格式化的参考表格:
| 数字 | 格式 | 输出 | 描述 |
| ---- | ---- | ---- | ---- |
| 3.1415926 | {:.2f} | 3.14 | 保留两位小数 |
| 3.1415926 | {:+.2f} | +3.14 | 保留两位小数并显示符号 |
| -1 | {:+.2f} | -1.00 | 保留两位小数并显示符号 |
| 3.1415926 | {:.0f} | 3 | 不保留小数(四舍五入) |
| 5 | {:0>2d} | 05 | 左侧用零填充 |
| 1000000 | {:,} | 1,000,000 | 用逗号分隔数字 |
| 0.25 | {:.2%} | 25.00% | 格式化为百分比 |
| 1000000000 | {:.2e} | 1.00e+09 | 科学计数法 |
| 11 | {:>10d} | 11 | 右对齐 |
| 11 | {:<10d} | 11 | 左对齐 |
| 11 | {:^10d} | 11 | 居中对齐 |
十进制数字的格式化
以下是一个打印随机数字的程序,未格式化时输出很不美观:
import random
for i in range(10):
x = random.randrange(20)
print(x)
使用字符串格式化可以让数字右对齐:
import random
for i in range(10):
x = random.randrange(20)
print("{:2}".format(x))
如果要处理大数字并添加逗号分隔符,可以这样做:
import random
for i in range(10):
x = random.randrange(100000)
print("{:6,}".format(x))
字符串的格式化
对于字符串的格式化,以下是一个例子:
my_fruit = ["Apples", "Oranges", "Grapes", "Pears"]
my_calories = [4, 300, 70, 30]
for i in range(4):
print("{:7} are {:3} calories.".format(my_fruit[i], my_calories[i]))
这个例子将水果名称和卡路里数进行了格式化输出。如果要改变对齐方式,可以使用
<
和
>
字符:
my_fruit = ["Apples", "Oranges", "Grapes", "Pears"]
my_calories = [4, 300, 70, 30]
for i in range(4):
print("{:>7} are {:<3} calories.".format(my_fruit[i], my_calories[i]))
前导零的使用
在显示时钟时间时,需要使用前导零。以下是未使用前导零和使用前导零的对比:
# 未使用前导零
for hours in range(1, 13):
for minutes in range(0, 60):
print("Time {}:{}".format(hours, minutes))
# 使用前导零
for hours in range(1, 13):
for minutes in range(0, 60):
print("Time {:02}:{:02}".format(hours, minutes))
浮点数的格式化
对于浮点数的格式化,可以控制精度和小数位数。以下是一个例子:
x = 0.1
y = 123.456789
print("{:.1} {:.1}".format(x, y))
print("{:.2} {:.2}".format(x, y))
print("{:.3} {:.3}".format(x, y))
print("{:.4} {:.4}".format(x, y))
print("{:.5} {:.5}".format(x, y))
print("{:.6} {:.6}".format(x, y))
print()
print("{:.1f} {:.1f}".format(x, y))
print("{:.2f} {:.2f}".format(x, y))
print("{:.3f} {:.3f}".format(x, y))
print("{:.4f} {:.4f}".format(x, y))
print("{:.5f} {:.5f}".format(x, y))
print("{:.6f} {:.6f}".format(x, y))
.2
表示显示两位精度的数字,
.2f
表示显示两位小数。还可以指定字段宽度:
x = 0.1
y = 123.456789
print("'{:10.1}' '{:10.1}'".format(x, y))
print("'{:10.2}' '{:10.2}'".format(x, y))
print("'{:10.3}' '{:10.3}'".format(x, y))
print("'{:10.4}' '{:10.4}'".format(x, y))
print("'{:10.5}' '{:10.5}'".format(x, y))
print("'{:10.6}' '{:10.6}'".format(x, y))
print()
print("'{:10.1f}' '{:10.1f}'".format(x, y))
print("'{:10.2f}' '{:10.2f}'".format(x, y))
print("'{:10.3f}' '{:10.3f}'".format(x, y))
print("'{:10.4f}' '{:10.4f}'".format(x, y))
print("'{:10.5f}' '{:10.5f}'".format(x, y))
print("'{:10.6f}' '{:10.6f}'".format(x, y))
通过递归和格式化的学习,我们可以更好地处理复杂的编程问题和优化输出结果。希望这些内容能帮助你在编程之路上更进一步。
递归与格式化:编程中的重要概念
格式化的高级应用
在实际编程中,格式化的应用场景丰富多样,除了前面提到的基本用法,还有一些高级技巧值得掌握。
动态格式化
有时候,我们需要根据程序运行时的情况动态地确定格式化的方式。例如,根据用户输入的精度要求来格式化浮点数。以下是一个示例代码:
x = 3.1415926
precision = int(input("请输入要保留的小数位数: "))
format_str = "{:." + str(precision) + "f}"
print(format_str.format(x))
在这个例子中,用户输入一个整数作为小数位数,程序根据这个输入动态生成格式化字符串,然后对浮点数
x
进行格式化输出。
格式化多个变量
我们可以在一个字符串中同时格式化多个变量,并且可以指定它们的顺序和格式。例如:
name = "Alice"
age = 25
height = 1.65
print("姓名: {0}, 年龄: {1:d}, 身高: {2:.2f} 米".format(name, age, height))
这里使用了索引
{0}
、
{1}
、
{2}
来指定变量的顺序,同时对年龄使用了整数格式
{1:d}
,对身高使用了保留两位小数的格式
{2:.2f}
。
递归与格式化的综合应用
递归和格式化可以结合起来解决一些复杂的问题。例如,在递归生成树形结构的数据时,我们可以使用格式化来美化输出。以下是一个简单的递归生成树形结构并格式化输出的示例:
class TreeNode:
def __init__(self, value, children=None):
self.value = value
self.children = children if children is not None else []
def print_tree(node, level=0):
indent = " " * level
print("{}{}".format(indent, node.value))
for child in node.children:
print_tree(child, level + 1)
# 构建一个简单的树
root = TreeNode(1, [
TreeNode(2, [TreeNode(4), TreeNode(5)]),
TreeNode(3)
])
print_tree(root)
在这个例子中,
TreeNode
类表示树的节点,
print_tree
函数是递归函数,它根据节点的层级使用空格进行缩进,从而实现树形结构的格式化输出。
递归与格式化的性能考虑
虽然递归和格式化在编程中非常有用,但在使用时也需要考虑性能问题。
递归的性能问题
递归函数在调用时会不断地在栈上创建新的函数调用帧,如果递归深度过大,会导致栈溢出错误。例如,前面提到的无限递归的例子:
def f():
print("Hello")
f()
f()
为了避免这种情况,我们需要控制递归深度,就像前面介绍的控制递归深度的例子一样。另外,递归函数可能会有重复计算的问题,例如在计算斐波那契数列时:
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
在计算
fibonacci(10)
时,会多次重复计算
fibonacci(3)
、
fibonacci(4)
等,导致性能下降。可以使用迭代或记忆化搜索等方法来优化。
格式化的性能问题
格式化字符串也会有一定的性能开销,尤其是在大量数据需要格式化时。例如,在循环中频繁使用格式化字符串:
for i in range(1000):
print("{:03d}".format(i))
可以考虑使用更高效的方法,如预先计算格式化字符串或使用
f-string
(Python 3.6 及以上版本支持):
for i in range(1000):
print(f"{i:03d}")
f-string
是一种更简洁和高效的字符串格式化方式,它在运行时直接替换变量的值,避免了
format
方法的一些开销。
总结
递归和格式化是编程中非常重要的概念,它们在不同的场景中都有广泛的应用。递归可以帮助我们解决一些具有自相似性的问题,如分形绘制、树形结构处理等;格式化可以让我们的输出更加美观和易读,提高程序的用户体验。
在使用递归时,要注意控制递归深度,避免栈溢出和重复计算;在使用格式化时,要根据具体情况选择合适的格式化方法,考虑性能因素。通过合理运用递归和格式化,我们可以编写出更加高效、简洁和易读的代码。
以下是一个总结递归和格式化关键要点的表格:
| 概念 | 要点 | 示例代码 |
| ---- | ---- | ---- |
| 递归 | 函数调用自身,需控制递归深度 |
python def f(level): if level < 10: f(level + 1) f(1)
|
| 格式化 | 美化输出,支持多种格式 |
python x = 3.14; print("{:.2f}".format(x))
|
| 递归与格式化结合 | 解决复杂问题,如树形结构输出 |
python class TreeNode: ... def print_tree(node, level=0): ...
|
| 性能考虑 | 递归注意栈溢出和重复计算,格式化选择高效方法 | 递归优化:记忆化搜索;格式化:使用
f-string
|
mermaid 格式流程图展示递归函数的执行流程:
graph TD;
A[开始] --> B{递归条件是否满足};
B -- 是 --> C[执行递归操作];
C --> B;
B -- 否 --> D[结束];
通过对递归和格式化的深入学习和实践,我们可以提升自己的编程能力,更好地应对各种编程挑战。希望这篇文章能对你有所帮助,让你在编程的道路上不断进步。
超级会员免费看

被折叠的 条评论
为什么被折叠?



