【问题标题】:Python- Sieve of Eratosthenes- Compact PythonPython-埃拉托色尼筛法-紧凑型 Python
【发布时间】:2023-03-27 12:25:02
【问题描述】:

这是我使用埃拉托色尼筛寻找素数的代码。

list = [i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]  

for i in list:
    for a in list:
            if a!=i and a%i == 0:
                list.remove(a)

试图找到一种方法将那些嵌套的 for 循环压缩为某种生成器或理解,但您似乎不能使用理解将函数应用于列表。我尝试使用地图和过滤器,但似乎无法正确使用。

考虑这样的事情:

print map(list.remove(a), filter(lambda a, i: (a%i ==0 and a!=i), [(a, i) for i in list for a in list])

显然由于十几个原因不起作用。如果我只是使用该代码的过滤器部分:

filter(lambda a, i: (a%i ==0 and a!=i), **[(a, i) for i in list for a in list]**

将两个变量放入 lambda 的正确方法是什么? (a,i) 使它成为一个元组,但我想将 'a' 和 'i' 作为独立变量提交到 lambda 中。

我最终解决了这个问题:

print sorted(set([i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]).difference(a for i in l for a in l if a!=i and a%i == 0))

【问题讨论】:

    标签: python algorithm performance primes


    【解决方案1】:

    首先要注意的是,你写的不是埃拉托色尼的筛子。看看一个完全幼稚的 Eratosthenes 筛子执行了多少循环:

    def sieve1(n):
        loops = 0
        numbers = set(range(2, n))
        for i in range(2, int(n ** 0.5) + 1):
            for j in range(i * 2, n, i):
                numbers.discard(j)
                loops += 1
        return sorted(numbers), loops
    

    测试:

    >>> sieve1(100)
    ([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 
      43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97], 
     178)
    

    100 个数字的 178 个循环(不包括排序)。这可以通过一些小的更改来改进:

    def sieve2(n):
        loops = 0
        numbers = range(0, n)
        for prime in numbers:
            if prime < 2:
                continue
            elif prime > n ** 0.5:
                break
            for i in range(prime ** 2, n, prime):
                numbers[i] = 0
                loops += 1
        return [x for x in numbers if x > 1], loops
    

    测试:

    >>> sieve2(100)
    ([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 
      43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97], 
     102)
    

    102 循环 100 个数字(不包括最后的过滤器)。现在看看你的:

    def sieve3(n):
        loops = 0
        numbers = range(2, n)
        for i in numbers:
            for j in numbers:
                if j != i and j % i == 0:
                    numbers.remove(j)
                loops += 1
        return numbers, loops
    

    测试:

    >>> sieve3(100)
    ([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 
      43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97], 
     663)
    

    情况变得更糟:

    >>> [sieve1(x)[1] for x in [100, 1000, 10000]]
    [178, 2978, 41723]
    >>> [sieve2(x)[1] for x in [100, 1000, 10000]]
    [102, 1409, 16979]
    >>> [sieve3(x)[1] for x in [100, 1000, 10000]]
    [663, 28986, 1523699]
    

    n = 10000,您的实现工作量几乎是原来的 100 倍!

    我的建议是在使其“紧凑”之前创建一个合理的实现。代码高尔夫可以很有趣,但它远没有编写高效代码那样具有挑战性或启发性,无论长度如何。

    也就是说,考虑一下这个单行(如果你不计算导入,你可以通过使用lambda x, y: x - y 代替operator.sub 来摆脱它)。这实现了第一个算法,但有一点小改进:

    >>> from operator import sub
    >>> reduce(sub, (set(range(x ** 2, 100, x)) for x in range(2, int(100 ** 0.5) + 1)), set(range(2, 100)))
    set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97])
    

    【讨论】:

    • 这令人印象深刻且非常有启发性。我是一个编程爱好者,所以我为它的低效率道歉:-\
    • @Parseltongue,无需为此道歉——每个人都在编写低效的代码! 我的 道歉,如果我生硬的话——我关心的不是效率,而是应该prioritize as a programmer。公平地说,有时以紧凑为目标很重要。只是过度压缩的代码很少是理想的。
    【解决方案2】:

    这不是你的循环的直接翻译,但它非常接近和紧凑:

    >>> l = range(2, 101)
    >>> sorted(set(l).difference(a for i in l for a in l if a!=i and a%i == 0))
    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
    

    虽然我建议 a &gt; i 而不是 a != 0 更短更快;)

    【讨论】:

    • "TypeError: 'type' object is not iterable" - 知道为什么吗?
    • @Parseltongue,我敢打赌这是因为您使用 list——即内置列表构造函数——作为变量名。
    • 我同意。如所要求的,我的目标只是对原始代码的简洁表达。这个网站上还有很多其他问题描述了寻找素数的更有效方法!
    • 此实现的运行时间非常短(因此不能合理地称为 Eratosthenes 的筛子)。当然,这对于少数人来说是一种简洁的方式,这就是我没有投反对票的原因。 =)
    【解决方案3】:

    你不是在做埃拉托色尼筛法;不正确实施算法的危险在于它会非常慢。例如,在 10**6 上尝试您的算法。

    我能想到的有界埃拉托色尼筛法的最短实现:

    def primes(upTo):
        isPrime = list(range(upTo))
        for p in range(2,int(upTo**0.5)+1): #p: 2,3,4,...,sqrt(N)
            print(p, isPrime[p])
            if isPrime[p]:
                for multiple in range(p**2,upTo,p): #mult: p^2, p^2+p, p^2+2p, ..., N
                    isPrime[multiple] = False
        return [x for x in isPrime[2:] if x]
    

    演示:

    >>> list(primes(29))
    [2, 3, 5, 7, 11, 13, 17, 19, 23]
    

    如果你忽略换行符和大量的跳过偶数优化,它实际上相当简洁:

    isPrime=[True]*upTo for p in range(2,upTo): if isPrime[p]: yield p for m in range(p,upTo,p): isPrime[m]=False
    

    【讨论】:

    • 花了我一段时间才理解,但这是一个非常聪明的实现。
    • 谢谢,但归功于 Eratosthenes =): en.wikipedia.org/wiki/… 如果算法不严格遵循这种形式,它很可能是错误的,因此非常慢。不幸的是,我遗漏了一个优化(为了可读性),如果p &gt; sqrt(upTo),则停止删除倍数。
    【解决方案4】:

    这是迄今为止我想出的最紧凑的真筛子。这表现得非常好。

    def pgen(n): # Sieve of Eratosthenes generator
        np = set() # keeps track of composite (not prime) numbers
        for q in xrange(2, n+1):
            if q not in np:
                yield q
                np.update(range(q*q, n+1, q))
    
    >>> list(pgen(100))
    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
    79, 83, 89, 97]            
    

    这个稍微复杂一点的版本是我见过的最快的:

    def pgen(n): # Sieve of Eratosthenes generator by Dan Salmonsen
        yield 2
        np = set()
        for q in xrange(3, n+1, 2):
            if q not in np:
                yield q
                np.update(range(q*q, n+1, q+q))            
    

    这是一个真正的筛子作为列表理解:

    def primes(n):
        sieve = set(sum([range(q*q, n+1, q+q) for q in odds], []))
        return [2] + [p for p in odds if p not in sieve]
    
    >>> primes(100)
    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
    79, 83, 89, 97]            
    

    【讨论】:

      【解决方案5】:

      以下单行代码与您的代码完全无关:

      def primes(n):
          return set(range(2,n))-{c for i in range(2,n) for c in range(2*i,n,i)}
      

      就像你的代码一样,这仍然是不是真的埃拉托色尼筛法,因为例如,它会徒劳地尝试划掉69 等的倍数。但它仍然可以运行对于小于一百万或更多的值,明显比大多数其他类似 Sieve 的相似,因为对于小 N,素数与非素数“大约一样多”(数字 1/log(N))。

      对源代码进行了大量修改,效率可能低于原始代码:http://codeblog.dhananjaynene.com/2011/06/10-python-one-liners-to-impress-your-friends/

      【讨论】:

        【解决方案6】:

        这是筛子的简单演示。请注意,lambda 不用作过滤函数,因为素数需要在定义时绑定。同样有趣的是,它在不重复部门的意义上是有效的,但从长远来看,它可能会导致you-know-what

        import itertools
        
        def primes():
            ints = itertools.count(2)
            while True:
                p = next(ints)
                yield p
                ints = itertools.ifilter(p.__rmod__, ints)
        
        print list(itertools.islice(primes(), 10))
        # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
        

        【讨论】:

          【解决方案7】:
          def sieve(n):
              sieve_list = range(n)
              zero_list = [0] * n
              for i in range(2, int(n**.5) + 1):
                  if sieve_list[i]:
                      sieve_list[2*i:n:i] = zero_list[2*i:n:i]
              return filter(None, sieve_list)[1:]
          

          【讨论】:

            【解决方案8】:

            不完全是最紧凑的解决方案,但 Python3 中 range 函数中的 step 参数在这里有所帮助 -

            prime_sieve = [True] * (int(input('Primes Upto ?'))+1)
            # The first prime number
            for i in range(2, len(prime_sieve)):
                if prime_sieve[i]:
                    for j in range(i+i, len(prime_sieve), i):
                        prime_sieve[j] = False
                    print(i, end=',')
            

            【讨论】:

              猜你喜欢
              • 2013-10-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-08-02
              • 2011-12-16
              • 2018-09-30
              相关资源
              最近更新 更多