• Python:用filter函数求素数 (再理解generator)


    目的:更熟悉应用generator。

    参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017404530360000

    素数定义:

    素数:质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数。

    方法:

    计算素数的一个方法是埃氏筛法

    首先,列出从2开始的所有自然数,构造一个序列:

    2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...

    第二步,取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉:

    3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...

    ⚠️此时的序列是从3开始的奇数集合。

    第三步,取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉:

    5, 6, 7, 8910, 11, 12, 13, 141516, 17, 18, 19, 20, ...

    第四步,取新序列的第一个数5,然后用5把序列的5的倍数筛掉:

    7, 8910, 11, 12, 13, 141516, 17, 18, 19, 20, ...

    后续,不断筛下去,就可以得到所有的素数。

    代码:

    2是素数,直接从一个奇数序列开始筛选:

    # 生成一个奇数序列。从3开始
    def _odd_iter():
        n = 1
        while True:
            n += 2
            yield n

     筛选函数:

    1. 由上面的分析可知,每次筛选,都除以序列的第一数。
    2. 条件是x % n > 0。x为序列中被判断的数字,n为该序列的第一个数字。整除结果==0的都不是素树。要从序列中删除。
    3. 所以,判断表达式是不固定的。定义一个函数,传入参数n, 返回一个lambda表达式用于判断。 
    # 传入参数n,生成不同的lambda表达式。
    def _not_divisible(n):
        # 返回一个lambda表达式。用于判断x,是不是素数。
        # 素数,只能被1和自身整除的自然数叫做素数。
        # 整除就是余数为0。
        # 如果被非自身数字整除,即余数为0,那自然不是素数,删除掉。
        return lambda x: x % n > 0

    主函数:

    1. 第一行代码:yield 2,返回素数2。
    2. 第二行代码:创建生成器,第一次运行,它产生奇数序列,这个序列会逐步被筛选,去掉非素数字。
    3. 循环代码:
      1. n = next(it), 得到序列的第一个数字。
      2. yield n,  它是素数。
      3. 用filter()来筛选, it生成器中的数字。 因为it是可迭代的。并把新的序列赋值给it变量。

    ⚠️重新理解3。filter函数对生成器对象it进行筛选,其实就是代码组合,把_not_divisible(n)返回的lambda表达式和it自身的代码组合,形成新的对象<filter object>。

    ⚠️我的理解是,原先的_odd_iter()产生的算法不再用,而是使用增加了lambda条件判断的新的算法,<filter object>。

    ⚠️这一步并不是真的筛选掉不符合的数字,_ood_iter()只是代表了生成序列数字的算法。只不过这个算法被新的算法替代了。

    def primes():
        yield 2  #2是素数。
        it = _odd_iter() #创建一个<generator object _odd_iter>,代表奇数序列。
        while True:
            n = next(it) #返回奇数序列的一个数。
            yield n  #这个数是素数。所以返回。
            # 创建一个<filter object>, 代表新序列。
            it = filter(_not_divisible(n), it)

    由于primes()所代表的算法生成的是一个无限序列,没有退出机制,所以调用时需要设置一个退出循环的条件:

    for n in primes():
        if n < 1000:
            print(n)
        else:
            break

    ⚠️primes本身是生成器函数, primes()就是一个生成器,它自身不储存数据,只储存生成数据的算法。 

    总结;

    由本例可知,generator其实就是算法,它不会一次性产生全部数据,而是根据调用的次数,逐步向内存写入数据。

    本例把一个生成器对象作为参数传入了filter()函数,其实就是对这个生成器的代码进行更新,即改进算法。最后返回一个新的迭代器。本例返回的是<filter object>。

  • 相关阅读:
    聚合支付里各扫码支付的返回报文样例
    短信平台接口安全控制
    「美团外卖APP签约快捷支付」流程体验
    多模块项目提示“Module ** must not contain source root **. The root already belongs to module **”的解决办法
    比较两种方式的form请求提交
    Linux screen命令和系统日志
    Linux 守护进程
    Linux 进程的通信方式与信号:kill命令
    Linux 进程的控制与进程之间的关系
    Linux 使用ps和top命令查看进程
  • 原文地址:https://www.cnblogs.com/chentianwei/p/11799867.html
Copyright © 2020-2023  润新知