14、Python编程:文件处理与递归算法实践

Python编程:文件处理与递归算法实践

1. 文件处理相关练习

1.1 一致行长度格式化

在终端窗口中显示文本时,不同的窗口宽度可能会导致文本显示效果不佳。当文本行过长时会自动换行,影响阅读;过短时又不能充分利用空间。为了解决这个问题,我们需要编写一个程序,该程序可以打开文件并将其内容进行格式化,使得每行尽可能地填满。

例如,对于包含如下内容的文件:

Alice was
beginning to get very tired of sitting by her
sister
on the bank, and of having nothing to do: once
or twice she had peeped into the book her sister
was reading, but it had
no
pictures or conversations in it,"and what is
the use of a book," thought Alice, "without
pictures or conversations?"

当设置行长度为 50 个字符时,输出应该如下:

Alice was beginning to get very tired of sitting
by her sister on the bank, and of having nothing
to do: once or twice she had peeped into the book
her sister was reading, but it had no pictures or
conversations in it, "and what is the use of a
book," thought Alice, "without pictures or
conversations?"

操作步骤如下:
1. 打开文件,逐行读取内容。
2. 对于每行内容,将其拆分为单词。
3. 尝试将单词添加到当前行,直到达到最大行长度。
4. 如果当前行已满,开始新的一行,并继续添加剩余的单词。
5. 注意处理文件中可能存在的多个段落,通过检测空行来区分段落。

1.2 查找包含特定元音顺序的单词

英语中存在至少一个单词,它恰好按顺序包含元音 A、E、I、O、U 和 Y 各一次。我们需要编写一个程序,该程序可以搜索包含单词列表的文件,并显示所有满足此条件的单词。

操作步骤如下:
1. 获取用户输入的文件名。
2. 尝试打开文件,如果文件不存在或无法打开,显示相应的错误信息并退出程序。
3. 逐行读取文件中的单词。
4. 检查每个单词是否包含元音 A、E、I、O、U 和 Y 各一次且按顺序排列。
5. 如果满足条件,则显示该单词。

2. 递归算法基础

递归是一种强大的编程技术,它允许函数调用自身来解决问题。一个递归定义必须以一个不同(通常是更小或更简单)的版本来描述被定义的事物,并且要朝着已知解决方案的问题版本前进。

递归函数通常包含至少一个基本情况(base case)和一个或多个递归情况(recursive case)。基本情况是函数可以直接返回结果而无需再次调用自身的情况;递归情况则是函数调用自身来解决更小版本问题的情况。

2.1 整数求和

计算从 0 到某个正整数 n 的所有整数之和可以使用循环、公式或递归方法。下面是使用递归方法实现的代码:

# Compute the sum of the integers from 0 up to and including n using recursion
# @param n the maximum value to include in the sum
# @return the sum of the integers from 0 up to and including n
def sum_to(n):
    if n <= 0:
        return 0  # Base case
    else:
        return n + sum_to(n - 1)  # Recursive case

# Compute the sum of the integers from 0 up to and including a value entered by the user
num = int(input("Enter a non-negative integer: "))
total = sum_to(num)
print("The total of the integers from 0 up to and including", num, "is", total)

当用户输入 2 时,程序的执行过程如下:
1. sum_to(2) 被调用,由于 n 大于 0,执行递归情况,调用 sum_to(1)
2. sum_to(1) 被调用,同样由于 n 大于 0,再次执行递归情况,调用 sum_to(0)
3. sum_to(0) 被调用,此时满足基本情况,直接返回 0。
4. sum_to(1) 接收到 sum_to(0) 返回的 0,将 1 加上 0 后返回 1。
5. sum_to(2) 接收到 sum_to(1) 返回的 1,将 2 加上 1 后返回 3。
6. 最终输出结果为 3。

2.2 斐波那契数列

斐波那契数列是一个整数序列,它以 0 和 1 开始,后续的每个数都是前两个数的和。前 10 个斐波那契数是 0、1、1、2、3、5、8、13、21 和 34。

下面是使用递归方法计算斐波那契数列的代码:

# Compute the nth Fibonacci number using recursion
# @param n the index of the Fibonacci number to compute
# @return the nth Fibonacci number
def fib(n):
    # Base cases
    if n == 0:
        return 0
    if n == 1:
        return 1
    # Recursive case
    return fib(n - 1) + fib(n - 2)

# Compute the Fibonacci number requested by the user
n = int(input("Enter a non-negative integer: "))
print("fib(%d) is %d." % (n, fib(n)))

这个递归算法虽然简洁,但效率较低。例如,计算 fib(35) 在现代计算机上可以很快返回结果,但计算 fib(70) 可能需要数年时间。因此,对于较大的斐波那契数,通常使用循环或公式来计算。

2.3 字符计数

递归不仅可以用于处理整数问题,还可以用于处理字符串问题。例如,计算一个字符串中特定字符的出现次数。

下面是使用递归方法实现字符计数的代码:

# Count the number of times a particular character is present in a string
# @param s the string in which the characters are counted
# @param ch the character to count
# @return the number of occurrences of ch in s
def count(s, ch):
    if s == "":
        return 0  # Base case
    # Compute the tail of s
    tail = s[1: len(s)]
    # Recursive cases
    if ch == s[0]:
        return 1 + count(tail, ch)
    else:
        return count(tail, ch)

# Count the number of times a character entered by the user occurs in a string entered
# by the user
s = input("Enter a string: ")
ch = input("Enter the character to count: ")
print("'%s' occurs %d times in '%s'" % (ch, count(s, ch), s))

操作步骤如下:
1. 定义基本情况:如果字符串为空,返回 0。
2. 计算字符串的尾部(除第一个字符外的所有字符)。
3. 如果第一个字符等于要计数的字符,则返回 1 加上尾部字符串中该字符的出现次数。
4. 否则,返回尾部字符串中该字符的出现次数。

2.4 递归算法复杂度对比

函数 输入增加 1 时函数调用次数增长情况 复杂度类型
sum_to 函数调用次数增加 1 线性增长
fib 函数调用次数(几乎)翻倍 指数增长

从复杂度对比可以看出,不同的递归算法在效率上可能存在很大差异。虽然递归在某些情况下可能效率较低,但在其他情况下,如欧几里得算法计算最大公约数,递归算法可以非常高效。

下面是 sum_to fib 函数调用次数增长情况的 mermaid 流程图:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B(输入 n):::process
    B --> C{选择函数}:::decision
    C -->|sum_to| D(计算 sum_to(n)):::process
    C -->|fib| E(计算 fib(n)):::process
    D --> F(函数调用次数线性增长):::process
    E --> G(函数调用次数指数增长):::process
    F --> H([结束]):::startend
    G --> H

3. 递归算法相关练习

3.1 数值求和

编写一个程序,从用户那里读取数值,直到用户输入一个空行为止,然后显示所有输入数值的总和(如果第一个输入就是空行,则总和为 0.0)。要求使用递归方法完成此任务,程序中不能使用任何循环。

操作步骤如下:
1. 定义一个递归函数,函数无需参数,但需要返回一个数值结果。
2. 在函数内部读取用户输入的一个值。
3. 判断输入是否为空行:
- 如果是,则返回 0.0。
- 如果不是,则将该值转换为数值类型,并加上递归调用该函数的结果。

以下是实现代码:

def total_values():
    value = input("Enter a value (blank line to quit): ")
    if value == "":
        return 0.0
    else:
        return float(value) + total_values()

result = total_values()
print("The total of all values entered is", result)

3.2 最大公约数计算

欧几里得算法是一种高效的递归算法,用于计算两个正整数 a 和 b 的最大公约数。其算法步骤如下:
1. 如果 b 为 0,则返回 a。
2. 否则,计算 a 除以 b 的余数 c。
3. 返回 b 和 c 的最大公约数。

以下是实现代码:

def gcd(a, b):
    if b == 0:
        return a
    else:
        c = a % b
        return gcd(b, c)

num1 = int(input("Enter the first positive integer: "))
num2 = int(input("Enter the second positive integer: "))
print("The greatest common divisor of", num1, "and", num2, "is", gcd(num1, num2))

3.3 十进制转二进制

将非负十进制数转换为二进制数可以使用递归方法。操作步骤如下:
1. 定义基本情况:当 n 为 0 或 1 时,直接返回对应的字符串 “0” 或 “1”。
2. 对于其他正整数 n,计算 n 除以 2 的余数作为下一位二进制数。
3. 递归调用函数计算 n // 2 的二进制表示。
4. 将递归调用的结果和当前余数拼接起来并返回。

以下是实现代码:

def decimal_to_binary(n):
    if n == 0:
        return "0"
    elif n == 1:
        return "1"
    else:
        remainder = str(n % 2)
        return decimal_to_binary(n // 2) + remainder

num = int(input("Enter a non - negative integer: "))
if num < 0:
    print("Error: Please enter a non - negative integer.")
else:
    print("The binary representation of", num, "is", decimal_to_binary(num))

3.4 北约音标字母表转换

北约音标字母表是一种广泛使用的拼写字母表,每个字母对应一个特定的单词。编写一个程序,读取用户输入的单词,并显示其北约音标拼写。要求使用递归方法,程序中不能使用任何循环。

操作步骤如下:
1. 定义一个字典,存储字母和对应的北约音标单词。
2. 定义递归函数,接收一个字符串作为参数。
3. 如果字符串为空,返回空字符串。
4. 否则,获取字符串的第一个字符,查找其对应的北约音标单词。
5. 递归调用函数处理字符串的剩余部分,并将结果和当前单词拼接起来返回。

以下是实现代码:

nato_alphabet = {
    'A': 'Alpha', 'B': 'Bravo', 'C': 'Charlie', 'D': 'Delta', 'E': 'Echo',
    'F': 'Foxtrot', 'G': 'Golf', 'H': 'Hotel', 'I': 'India', 'J': 'Juliet',
    'K': 'Kilo', 'L': 'Lima', 'M': 'Mike', 'N': 'November', 'O': 'Oscar',
    'P': 'Papa', 'Q': 'Quebec', 'R': 'Romeo', 'S': 'Sierra', 'T': 'Tango',
    'U': 'Uniform', 'V': 'Victor', 'W': 'Whiskey', 'X': 'Xray', 'Y': 'Yankee',
    'Z': 'Zulu'
}

def nato_spelling(word):
    word = word.upper()
    if word == "":
        return ""
    first_char = word[0]
    if first_char in nato_alphabet:
        return nato_alphabet[first_char] + " " + nato_spelling(word[1:])
    else:
        return nato_spelling(word[1:])

user_word = input("Enter a word: ")
print(nato_spelling(user_word).strip())

4. 总结

递归是一种强大的编程技术,在解决某些问题时非常有效。通过本文介绍的多个示例,我们可以看到递归在不同场景下的应用,包括整数求和、斐波那契数列计算、字符计数等。同时,我们也了解到不同递归算法在效率上可能存在巨大差异,如 sum_to 函数是线性增长,而 fib 函数是指数增长。

在实际编程中,我们需要根据具体问题选择合适的算法。对于一些问题,递归可以提供简洁的解决方案;而对于另一些问题,可能需要使用循环或其他方法来提高效率。

以下是一个简单的总结表格,对比不同递归练习的特点:
| 练习名称 | 功能 | 复杂度 | 注意事项 |
| ---- | ---- | ---- | ---- |
| 数值求和 | 计算用户输入数值的总和 | 线性 | 不能使用循环,需处理空行输入 |
| 最大公约数计算 | 计算两个正整数的最大公约数 | 高效 | 利用欧几里得算法的递归特性 |
| 十进制转二进制 | 将非负十进制数转换为二进制数 | 线性 | 处理 0 和 1 的特殊情况 |
| 北约音标字母表转换 | 将单词转换为北约音标拼写 | 线性 | 忽略非字母字符,使用递归处理字符串 |

下面是一个 mermaid 流程图,展示递归算法选择的基本思路:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B(明确问题):::process
    B --> C{问题是否可分解为子问题}:::decision
    C -->|是| D{子问题是否与原问题相似}:::decision
    C -->|否| E(考虑其他方法):::process
    D -->|是| F{是否有基本情况}:::decision
    D -->|否| E
    F -->|是| G(使用递归解决):::process
    F -->|否| E
    G --> H([结束]):::startend
    E --> H
【电能质量扰动】基于ML和DWT的电能质量扰动分类方法研究(Matlab实现)内容概要:本文研究了一种基于机器学习(ML)和离散小波变换(DWT)的电能质量扰动分类方法,并提供了Matlab实现方案。首先利用DWT对电能质量信号进行多尺度分解,提取信号的时频域特征,有效捕捉电压暂降、暂升、中断、谐波、闪变等常见扰动的关键信息;随后结合机器学习分类器(如SVM、BP神经网络等)对提取的特征进行训练分类,实现对不同类型扰动的自动识别准确区分。该方法充分发挥DWT在信号去噪特征提取方面的优势,结合ML强大的模式识别能力,提升了分类精度鲁棒性,具有较强的实用价值。; 适合人群:电气工程、自动化、电力系统及其自动化等相关专业的研究生、科研人员及从事电能质量监测分析的工程技术人员;具备一定的信号处理基础和Matlab编程能力者更佳。; 使用场景及目标:①应用于智能电网中的电能质量在线监测系统,实现扰动类型的自动识别;②作为高校或科研机构在信号处理、模式识别、电力系统分析等课程的教学案例或科研实验平台;③目标是提高电能质量扰动分类的准确性效率,为后续的电能治理设备保护提供决策依据。; 阅读建议:建议读者结合Matlab代码深入理解DWT的实现过程特征提取步骤,重点关注小波基选择、分解层数设定及特征向量构造对分类性能的影响,并尝试对比不同机器学习模型的分类效果,以全面掌握该方法的核心技术要点。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值