• 第9关:生成器与 yield


    任务描述

    Python 中存在着一个特殊的函数:生成器。生成器是一个“函数对象”,它与函数的定义在形式上完全相同,具有“函数名”与“参数列表”,不同之处在于它可以以yield方式“暂时返回”。

    本关的任务是让学习者掌握 Python 中生成器的使用方法,然后利用生成器实现一个计算 π 的具有 O(N) 复杂度的算法。

    相关知识
    生成器的特点

    当从别处调用的生成器暂时返回后,生成器会记住上次的返回点;当对该生成器再次调用(使用next()方法)时,它会从生成器上次的返回点继续执行,而不是从头开始执行该被调函数;此外,上次调用返回时的“上下文”在下次调用时也会恢复。

    恰当的使用生成器,可以有效地降低使用标准函数时的计算复杂度。下面以斐波那契函数为例介绍如何使用生成器来实现。

    基于生成器的斐波那契函数实现

    斐波那契数列中的第 n 项 F(n) 可由下面的递推公式计算获得:

    如果要计算 ∑i=0n​F(i) 的值,大致有下面两种方法。

    第一种方法,按表达式通过定义函数计算:定义一个函数计算 F(i) ,而后用一个循环将这些计算结果累加。因为计算 F(i) 需要 O(i) 次加法,这使得整个运算的复杂度为 O(n2) 。

    第二种方法,使用全局变量:将当前的 iF(i)F(i+1) 分别保存在全局变量 iab 中,可以获得时间开销为 O(n) 的算法。然而,这种方法不具有扩展性 —— 当需要存储的全局变量非常多的时候,会破坏程序的数据封装性。

    我们可以使用一个生成器,在避免使用过多全局变量的情况下获得开销为 O(n) 的算法,代码如下。

    1. def fib():
    2. a = 1
    3. b = 1
    4. yield a # 第一次的返回值
    5. yield b # 第二次的返回值
    6. while True:
    7. a,b = b, a+b
    8. yield b # 后面项的返回值
    9. s = 0
    10. f = fib
    11. n = input()
    12. for i in range(n+1):
    13. s += next(f)
    14. print s

    利用一个跟踪调试器,理解上述代码的执行,并弄明白为什么只需要 O(n) 次计算。

    基于韦达公式的圆周率计算方法

    在微积分诞生之前,法国数学家韦达于 1593 年发表了关于 π 的一个计算公式,也就是 Vieta(韦达)公式 :

    那么就有 2π​≈∏i=0N​ai​ 。

    使用该公式,我们可以计算出 π 的值。

    编程要求

    本关的编程任务是利用生成器,基于韦达公式给出计算 π 的具有 O(N) 复杂度的算法。

    具体实现的代码框架如下:

    1. # coding:utf-8
    2. from math import sqrt
    3. def Vieta():
    4. #请在此输入代码
    5. #********** Begin *********#
    6. a = sqrt(2)/2.0
    7. while True:
    8. yield a
    9. a = sqrt((1.0+a)/2.0)
    10. #********** End *********#
    11. N = int(input())
    12. v = Vieta(); p = 1.0
    13. for i in range(N+1):
    14. # 请在此输入代码
    15. # ********** Begin *********#
    16. p = p*next(v)
    17. # ********** End *********#
    18. print ("%.6f"%(2.0/p))

    本实训测试样例如下:

    测试输入: 20

    预期输出: 3.141593

    提问:上述代码中为什么要用while true呢,意义在哪里,如果不加while true能行吗

    在上述代码中,使用while True创建了一个无限循环,这是为了生成器函数leibniz_generator()能够无限产生项的值。这是因为韦达公式中的级数是无限的,所以我们需要一个无限循环来不断生成这些项。

    如果不使用while True,而是使用某个固定的循环次数,那么就无法得到π的精确值,因为你无法确定需要多少项才能达到所需的精度。通过使用while True,我们可以根据需要生成尽可能多的项,直到满足精度要求为止。一旦某一项的值小于所需的精度,我们就可以在calculate_pi()函数中使用break语句来退出循环,从而计算出近似值。

    因此,while True在这里的意义是创建一个无限循环,使得生成器能够按需生成无限多的项,以便在满足精度条件时停止。如果不使用无限循环,代码将会限制了项数,无法获得所需的精确度。

  • 相关阅读:
    修改中文、英文参考文献在文末列表中的顺序:EndNote
    使用SpringBoot和ZXing实现二维码生成与解析
    HSV空间改进的多尺度Retinex算法
    如何实现云上 Lakehouse 高性能
    链路状态路由协议OSPF的LSA头部讲解
    MIT的智慧,利用深度学习来解决了交通堵塞
    @SpringBootApplication注解的理解——如何排除自动装配 & 分布式情况下如何自动加载 & nacos是怎么被发现的
    带网络变压器的RJ45网口连接器/集成RJ45网口连接器
    【leetcode】【2022/8/23】782. 变为棋盘
    Standard Template Libary or C++ Standard Library
  • 原文地址:https://blog.csdn.net/weixin_46305053/article/details/132944092