Python生成器实现杨辉三角

本文介绍使用Python生成器实现杨辉三角的方法。杨辉三角是一种二项式系数的几何排列,文中详细解释了其构成规律,并通过生成器实现逐层打印的功能。

杨辉三角

杨辉三角,是二项式系数在三角形中的一种几何排列。在欧洲,这个表叫做帕斯卡三角形。帕斯卡(1623----1662)是在1654年发现这一规律的,比杨辉要迟393年,比贾宪迟600年。(引自于:百度百科)

杨辉三角

很容易就得出规律: 除根以外,每个数都由它上层的左右两数之和,一个数不存在即视为0。根为1

由此规律,易得每层边界数值为1

下面用Python的生成器来实现这个著名的三角:)

生成器实现

# _*_ coding: utf-8 _*_

def yangHui(level):
    for i in range(level):
        # 由于根不符合抽象出的规律,单独处理
        if i == 0:
            yield [1]
            gen = yangHui(level)
        else:
            current_level = [1]
            prev_level = next(gen)
            try:
                for j in range(i):
                    current_level.append(prev_level[j] + prev_level[j+1])
            # 这里会产生`IndexError`异常,因为最右边只有一个加数了,j+1过界
            except:
                current_level.append(prev_level[j])
                #pass
            finally:
                #current_level.append(1)
                yield current_level

if __name__ == '__main__':
    for i in yangHui(10):
        print(i)                

运行结果如下:

wk:mysource mac$ python yanghui.py 
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
<think>我们已知杨辉三角的每个数等于它上方两数之和。递归方法可以定义函数来计算每个位置的值,但直接递归会导致大量重复计算。 我们可以结合生成器来避免重复计算,即在生成器函数中利用递归思想逐行生成,同时存储已生成的中间结果(避免重复递归计算)。 然而,用户要求使用递归方式实现生成器生成器是逐行产生的,我们可以设计一个递归函数来生成每一行,但需要注意递归的深度(行数)和重复计算问题。 根据引用[3]的提示,直接递归计算每个值而不存储中间结果会导致重复计算,效率低下。因此,我们可以考虑使用记忆化存储(缓存)来优化递归计算过程,或者设计一个递归函数来生成整个三角形(但这样可能无法逐行生成)。 但是生成器要求我们逐行生成。一种思路是:我们设计一个递归函数,它能够返回第n行的数据(利用递归得到前一行,再计算当前行)。这样我们可以从第0行开始递归到第n行,然后逐行生成。 具体步骤: 1. 基本情况:第0行为[1] 2. 递归情况:已知第k-1行,则第k行可以通过第k-1行计算得到(每个元素等于上一行的相邻两元素之和,首尾为1) 这样,我们可以在生成器函数中使用递归来生成每一行,但需要注意递归深度(行数)不能太大,否则可能导致栈溢出。另外,由于每一行依赖前一行,我们可以用递归函数返回上一行,然后计算当前行。 然而,用户要求将递归和生成器结合。我们可以这样设计: - 生成器函数本身不递归,而是调用一个递归函数来逐行获取数据(但这样递归函数需要知道如何生成第n行,并且依赖第n-1行,因此递归函数需要计算第n-1行,然后再计算第n行,如此递归下去直到第0行)。 - 但这样递归函数需要保存中间行,我们可以通过记忆化避免重复计算。 但是,生成器要求逐行产生,我们可以用循环来实现,而递归在这里似乎并不自然。 另一种思路:我们可以编写一个递归函数,它接收当前行号,并返回当前行的列表,然后利用递归调用上一行。在生成器函数中,我们从0开始递增行号,调用这个递归函数获取每一行。 但是,如果递归函数每次重新计算上一行,那么计算第n行时会递归计算第n-1行,而第n-1行又会递归计算第n-2行,这样就会重复计算。为了避免重复计算,我们可以使用缓存(例如lru_cache)。 下面是一个使用递归函数计算单行,并利用缓存优化的版本,然后在生成器中调用这个递归函数逐行生成。 定义递归函数`get_row(n)`,它返回杨辉三角第n行(n从0开始): - 当n=0时,返回[1] - 当n=1时,返回[1,1] - 对于n>1,第n行可以通过第n-1行计算得到:第k个元素等于第n-1行的第k-1个元素和第k个元素之和,首尾为1。 但是,递归函数`get_row(n)`需要调用`get_row(n-1)`。这样我们可以缓存中间结果。 然而,生成器要求我们使用生成器函数,所以我们可以这样做: from functools import lru_cache @lru_cache(maxsize=None) def get_row(n): if n == 0: return (1,) if n == 1: return (1,1) prev = get_row(n-1) row = [1] for i in range(1, n): row.append(prev[i-1] + prev[i]) row.append(1) return tuple(row) # 因为lru_cache要求可哈希,所以使用元组 def yanghui_triangle(rows): for i in range(rows): yield get_row(i) 但是,这种方法使用了递归来计算行,并且通过缓存避免了重复计算。但严格来说,生成器函数yanghui_triangle本身并没有递归,而是调用了递归函数。 用户要求的是“使用递归方式实现杨辉三角生成器”,我们这里递归体现在get_row函数中。 然而,引用[3]指出,递归计算每个位置的值(不缓存)会导致重复计算。因此,我们使用缓存优化递归函数get_row。 但是,我们也可以不使用缓存,而是通过递归生成整个三角形直到第n行,然后返回第n行?这样整个递归函数返回的是所有行的列表,但这样就不是生成器了。 我们需要的是生成器(逐行生成),所以我们的生成器函数应该每次只产生一行,而不是一次性生成所有行。 因此,我们采用上述方案:递归函数计算单行(带缓存),生成器循环调用该递归函数。 但是,用户可能希望看到一个不使用外部缓存而是通过递归结构自然生成每一行的方法?另一种递归思路: 我们可以设计一个递归函数,它生成从第0行到第n行的所有行,然后我们把它改造成生成器。但这样递归深度会随着行数增加而增加,而且我们需要在递归过程中产生数据(使用生成器递归)。 我们可以写一个递归生成器,但这样并不常见。例如: def yanghui_triangle_recursive(n): if n == 0: yield [1] else: # 递归获取前面的所有行 # 但是我们只需要前一行,所以我们可以只递归一次得到上一行 # 思路:先递归生成前面的所有行,然后计算当前行 # 但这样就不是逐行生成了,因为我们递归调用时已经生成了前面所有行 # 所以我们无法避免一次性生成所有行? 为了避免一次性生成所有行,我们需要在递归生成前面的行之后,继续生成当前行。但这样递归深度为n,而且需要保存前面所有的行(或者至少前一行)?实际上,如果我们递归生成前面的行,那么我们就需要保存前面所有行(因为生成器一旦产生,我们就不能回溯到上一行,除非我们存储了)。 因此,我们回到之前那个使用递归函数计算单行并缓存的方案。 考虑到用户要求的是“递归方式实现生成器”,我们可以这样理解:生成器本身不递归,但内部使用递归函数来计算每一行(利用缓存避免重复计算)。 下面给出代码示例: 注意:这里使用缓存是为了避免重复递归计算同一行,提高效率。 另外,我们也可以不使用递归计算每一行,而是用循环。但用户要求递归方式。 代码实现: </think>在Python中使用递归方式实现杨辉三角生成器时,需要结合递归计算和生成器的特性。以下是优化后的递归生成器实现,避免了重复计算问题: ```python def recursive_yanghui_generator(n): # 缓存字典,避免重复计算 cache = {} def calculate_element(row, col): """递归计算杨辉三角中给定位置的元素""" if col == 0 or col == row: # 每行首尾元素为1 return 1 if (row, col) in cache: # 检查缓存 return cache[(row, col)] # 核心递归关系:P(row,col) = P(row-1,col-1) + P(row-1,col) result = calculate_element(row-1, col-1) + calculate_element(row-1, col) cache[(row, col)] = result # 存储计算结果 return result # 生成器部分 for r in range(n): # 遍历每一行 yield [calculate_element(r, c) for c in range(r+1)] # 生成当前行 # 使用示例 rows = 5 print(f"递归生成器实现的{rows}行杨辉三角:") for row in recursive_yanghui_generator(rows): print(row) ``` ### 关键特性说明: 1. **递归计算元素**:核心函数`calculate_element()`递归实现杨辉三角的计算公式: $$P(row, col) = \begin{cases} 1 & \text{if } col=0 \text{ or } col=row \\ P(row-1,col-1) + P(row-1,col) & \text{otherwise} \end{cases}$$ 2. **缓存优化**:使用`cache`字典存储已计算结果,避免重复递归调用,解决直接递归的效率问题[^3]。 3. **生成器实现**: - 使用`yield`逐行生成结果,内存效率高 - 每次迭代生成一整行列表,格式整齐 ### 输出示例(rows=5时): ``` [1] [1, 1] [1, 2, 1] [1, 3, 3, 1] [1, 4, 6, 4, 1] ``` 此实现结合了递归的数学直观性和生成器的内存效率,相比直接递归减少约$O(2^n)$的时间复杂度,优化为$O(n^2)$级别[^1][^2]。对于需要处理大规模杨辉三角的场景,建议设置递归深度限制或改用迭代生成器实现
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值