Python中简单的质数生成器

67

请问我这段代码哪里做错了?它只是无论如何都打印出“count”。我只想要一个非常简单的质数生成器(不需要花哨的东西)。

import math

def main():
    count = 3
    one = 1
    while one == 1:
        for x in range(2, int(math.sqrt(count) + 1)):
            if count % x == 0: 
                continue
            if count % x != 0:
                print count

        count += 1

2
它没有终止吗?里面有一个“while one == 1:”并不奇怪。它没有产生任何输出吗?它会产生非质数吗?它太慢了吗?它不是C#吗?问题出在哪里? - S.Lott
2
如果这不是作业的话,您可能想了解一下埃氏筛法:http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes - CTT
我赞同CTT的评论。这样编码会同样容易,如果不是更容易的话。 - Himadri Choudhury
对于埃拉托斯特尼筛法的简单实现,请参见:https://dev59.com/snI95IYBdhLWcg3w-DH0 - Robert William Hanks
28个回答

213

存在一些问题:

  • 当数字不能被x整除时,为什么要打印计数?这并不意味着它是质数,只意味着该特定的x不能将其整除
  • continue会移动到下一个循环迭代 - 但你真正想要停止它使用break

以下是您的代码,经过一些修复,它只打印出质数:

import math

def main():
    count = 3
    
    while True:
        isprime = True
        
        for x in range(2, int(math.sqrt(count) + 1)):
            if count % x == 0: 
                isprime = False
                break
        
        if isprime:
            print count
        
        count += 1

如果想要更高效的质数生成,请参考其他人建议的埃拉托色尼筛选法。以下是一个优化良好且有注释的实现:

# Sieve of Eratosthenes
# Code by David Eppstein, UC Irvine, 28 Feb 2002
# http://code.activestate.com/recipes/117119/

def gen_primes():
    """ Generate an infinite sequence of prime numbers.
    """
    # Maps composites to primes witnessing their compositeness.
    # This is memory efficient, as the sieve is not "run forward"
    # indefinitely, but only as long as required by the current
    # number being tested.
    #
    D = {}
    
    # The running integer that's checked for primeness
    q = 2
    
    while True:
        if q not in D:
            # q is a new prime.
            # Yield it and mark its first multiple that isn't
            # already marked in previous iterations
            # 
            yield q
            D[q * q] = [q]
        else:
            # q is composite. D[q] is the list of primes that
            # divide it. Since we've reached q, we no longer
            # need it in the map, but we'll mark the next 
            # multiples of its witnesses to prepare for larger
            # numbers
            # 
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]
        
        q += 1

请注意,它返回一个生成器。


7
这个筛子非常简洁。它来自哪里? - SingleNegationElimination
3
这是一个非常出色的筛法实现。我以前从未见过它应用于无限范围,但事后看来很明显。 - Nick Johnson
2
@xiao 我认为,“in”操作的平均时间是恒定的,最坏情况下是线性的。 - yati sagade
7
如果您想知道这段代码的出处以及一些更快的改进方法(在我的测试中快了4倍),请查看此线程http://code.activestate.com/recipes/117119-sieve-of-eratosthenes/。 - Nick Craig-Wood
6
那么,怎样使用这个? - Ferdinando Randisi
显示剩余10条评论

27

Re非常强大:

import re


def isprime(n):
    return re.compile(r'^1?$|^(11+)\1+$').match('1' * n) is None

print [x for x in range(100) if isprime(x)]

###########Output#############
[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]

9
非常聪明!Explanation,对于那些感兴趣的人提供解释。 - georg
8
效率非常低。 - Amr Saber

18
def is_prime(num):
    """Returns True if the number is prime
    else False."""
    if num == 0 or num == 1:
        return False
    for x in range(2, num):
        if num % x == 0:
            return False
    else:
        return True

>> filter(is_prime, range(1, 20))
  [2, 3, 5, 7, 11, 13, 17, 19]
我们将在列表中获得所有小于20的质数。 我本可以使用埃拉托斯特尼筛法,但你说你想要非常简单的方法。;)

2
1 不是质数。2和3是质数,但不在这里。因此,对于前三个数字,这已经不能工作了。 - unbeknown
1
如果到达最大数值它将取模为0并返回false。 - humble_coder
else是不必要的。如果没有它,函数将返回True,表示它是一个质数,但这可能会让初学者感到困惑。 - user7050005
1
如果你将 for x in range(2, num) 改为 for x in range(2, math.trunc(math.sqrt(num)) + 1),那么你会得到相同的结果,但速度更快。 - Jonathan Hartley

11
def primes(n): # simple sieve of multiples 
   odds = range(3, n+1, 2)
   sieve = set(sum([list(range(q*q, n+1, q+q)) for q in odds], []))
   return [2] + [p for p in odds if p not in sieve]

>>> primes(50)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

测试一个数字是否为质数:

>>> 541 in primes(541)
True
>>> 543 in primes(543)
False

1
这并不是埃拉托色尼筛法,因为它通过枚举奇数的倍数来找到合数,而SoE则枚举质数的倍数。 - Will Ness
所以,几乎是埃拉托斯特尼筛法。仍然比试除法好得多... - Will Ness

10
print [x for x in range(2,100) if not [t for t in range(2,x) if not x%t]]

4
很简单,但不够高效。在一台典型的个人电脑上,使用range(10000)需要几秒钟的时间。 - kmonsoor

7

SymPy 是用于符号数学计算的 Python 库。它提供了几个函数来生成素数。

isprime(n)              # Test if n is a prime number (True) or not (False).

primerange(a, b)        # Generate a list of all prime numbers in the range [a, b).
randprime(a, b)         # Return a random prime number in the range [a, b).
primepi(n)              # Return the number of prime numbers less than or equal to n.

prime(nth)              # Return the nth prime, with the primes indexed as prime(1) = 2. The nth prime is approximately n*log(n) and can never be larger than 2**n.
prevprime(n, ith=1)     # Return the largest prime smaller than n
nextprime(n)            # Return the ith prime greater than n

sieve.primerange(a, b)  # Generate all prime numbers in the range [a, b), implemented as a dynamically growing sieve of Eratosthenes. 

这里有一些例子。
>>> import sympy
>>> 
>>> sympy.isprime(5)
True
>>> list(sympy.primerange(0, 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]
>>> sympy.randprime(0, 100)
83
>>> sympy.randprime(0, 100)
41
>>> sympy.prime(3)
5
>>> sympy.prevprime(50)
47
>>> sympy.nextprime(50)
53
>>> list(sympy.sieve.primerange(0, 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
这是一个(Python 2.6.2)简单的解决方案……符合原始请求(已有六个月),应该是任何“编程101”课程中完全可接受的解决方案……因此发布此帖。
import math

def isPrime(n):
    for i in range(2, int(math.sqrt(n)+1)):
        if n % i == 0: 
            return False;
    return n>1;

print 2
for n in range(3, 50):
    if isPrime(n):
        print n

这种简单的“暴力”方法对于现代PC上最多可处理约16,000的数字是足够快的(在我的2GHz机器上大概需要8秒左右)。显然,可以更有效率地完成此操作,方法是不要为每个数字重新计算每个偶数或每个3、5、7等的素数,参见Eratosthenes筛法(参见eliben的实现),或者如果你感到特别勇敢和/或疯狂,可以尝试Atkin筛法。请注意:我是Python新手,请勿把我说的话当作圭亚那四书五经。

3

这里是一个numpy版本的埃拉托色尼筛法,具有良好的复杂度(低于对长度为n的数组进行排序)和向量化。

import numpy as np 
def generate_primes(n):
    is_prime = np.ones(n+1,dtype=bool)
    is_prime[0:2] = False
    for i in range(int(n**0.5)+1):
        if is_prime[i]:
            is_prime[i*2::i]=False
    return np.where(is_prime)[0]

时间:

import time    
for i in range(2,10):
    timer =time.time()
    generate_primes(10**i)
    print('n = 10^',i,' time =', round(time.time()-timer,6))

>> n = 10^ 2  time = 5.6e-05
>> n = 10^ 3  time = 6.4e-05
>> n = 10^ 4  time = 0.000114
>> n = 10^ 5  time = 0.000593
>> n = 10^ 6  time = 0.00467
>> n = 10^ 7  time = 0.177758
>> n = 10^ 8  time = 1.701312
>> n = 10^ 9  time = 19.322478

1
https://zh.wikipedia.org/wiki/%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90#%E7%BB%8F%E9%AA%8C%E5%8D%95%E4%BD%8D%E6%97%B6%E9%97%B4 - Will Ness
1
顺便提一下 - 看看 n^6 和 n^7 之间的差异。这是由于缓存未命中造成的,在一百万条目上,它可以将数组保存在缓存中,但无法在一千万条目上做到这一点。https://en.wikipedia.org/wiki/CPU_cache - Peter Mølgaard Pallesen
不错。我一开始认为第一个度量值“太小”,但你实际上提供了一个真正的解释!经验增长阶是一种有价值的分析工具,我强烈推荐使用它。(我甚至发布了一个关于它的问题和答案,有关"画家难题",但迄今为止只有100个浏览量...)。也许如果它更“时髦”,疫情反应起初就不会那么缓慢了。 - Will Ness

2

另一个简单的例子,通过仅考虑奇数进行简单优化。使用懒惰流(Python生成器)完成所有操作。

用法:primes = list(create_prime_iterator(1, 30))

import math
import itertools

def create_prime_iterator(rfrom, rto):
    """Create iterator of prime numbers in range [rfrom, rto]"""
    prefix = [2] if rfrom < 3 and rto > 1 else [] # include 2 if it is in range separately as it is a "weird" case of even prime
    odd_rfrom = 3 if rfrom < 3 else make_odd(rfrom) # make rfrom an odd number so that  we can skip all even nubers when searching for primes, also skip 1 as a non prime odd number.
    odd_numbers = (num for num in xrange(odd_rfrom, rto + 1, 2))
    prime_generator = (num for num in odd_numbers if not has_odd_divisor(num))
    return itertools.chain(prefix, prime_generator)

def has_odd_divisor(num):
    """Test whether number is evenly divisable by odd divisor."""
    maxDivisor = int(math.sqrt(num))
    for divisor in xrange(3, maxDivisor + 1, 2):
        if num % divisor == 0:
            return True
    return False

def make_odd(number):
    """Make number odd by adding one to it if it was even, otherwise return it unchanged"""
    return number | 1

2

我刚学习了这个主题,在帖子中寻找示例并尝试制作我的版本:

from collections import defaultdict
# from pprint import pprint

import re


def gen_primes(limit=None):
    """Sieve of Eratosthenes"""
    not_prime = defaultdict(list)
    num = 2
    while limit is None or num <= limit:
        if num in not_prime:
            for prime in not_prime[num]:
                not_prime[prime + num].append(prime)
            del not_prime[num]
        else:  # Prime number
            yield num
            not_prime[num * num] = [num]
        # It's amazing to debug it this way:
        # pprint([num, dict(not_prime)], width=1)
        # input()
        num += 1


def is_prime(num):
    """Check if number is prime based on Sieve of Eratosthenes"""
    return num > 1 and list(gen_primes(limit=num)).pop() == num


def oneliner_is_prime(num):
    """Simple check if number is prime"""
    return num > 1 and not any([num % x == 0 for x in range(2, num)])


def regex_is_prime(num):
    return re.compile(r'^1?$|^(11+)\1+$').match('1' * num) is None


def simple_is_prime(num):
    """Simple check if number is prime
    More efficient than oneliner_is_prime as it breaks the loop
    """
    for x in range(2, num):
        if num % x == 0:
            return False
    return num > 1


def simple_gen_primes(limit=None):
    """Prime number generator based on simple gen"""
    num = 2
    while limit is None or num <= limit:
        if simple_is_prime(num):
            yield num
        num += 1


if __name__ == "__main__":
    less1000primes = list(gen_primes(limit=1000))
    assert less1000primes == list(simple_gen_primes(limit=1000))
    for num in range(1000):
        assert (
            (num in less1000primes)
            == is_prime(num)
            == oneliner_is_prime(num)
            == regex_is_prime(num)
            == simple_is_prime(num)
        )
    print("Primes less than 1000:")
    print(less1000primes)

    from timeit import timeit

    print("\nTimeit:")
    print(
        "gen_primes:",
        timeit(
            "list(gen_primes(limit=1000))",
            setup="from __main__ import gen_primes",
            number=1000,
        ),
    )
    print(
        "simple_gen_primes:",
        timeit(
            "list(simple_gen_primes(limit=1000))",
            setup="from __main__ import simple_gen_primes",
            number=1000,
        ),
    )
    print(
        "is_prime:",
        timeit(
            "[is_prime(num) for num in range(2, 1000)]",
            setup="from __main__ import is_prime",
            number=100,
        ),
    )
    print(
        "oneliner_is_prime:",
        timeit(
            "[oneliner_is_prime(num) for num in range(2, 1000)]",
            setup="from __main__ import oneliner_is_prime",
            number=100,
        ),
    )
    print(
        "regex_is_prime:",
        timeit(
            "[regex_is_prime(num) for num in range(2, 1000)]",
            setup="from __main__ import regex_is_prime",
            number=100,
        ),
    )
    print(
        "simple_is_prime:",
        timeit(
            "[simple_is_prime(num) for num in range(2, 1000)]",
            setup="from __main__ import simple_is_prime",
            number=100,
        ),
    )

这段代码的运行结果展示了有趣的结果:
$ python prime_time.py
Primes less than 1000:
[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, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]

Timeit:
gen_primes: 0.6738066330144648
simple_gen_primes: 4.738092333020177
is_prime: 31.83770858097705
oneliner_is_prime: 3.3708438930043485
regex_is_prime: 8.692703998007346
simple_is_prime: 0.4686249239894096

我可以看到我们有不同问题的正确答案;对于一个质数生成器,gen_primes 看起来是正确的答案;但对于一个质数检查,simple_is_prime 函数更适合。

这个方法可行,但我总是愿意采用更好的方式来制作 is_prime 函数。


我有一个相对快速的质数生成器(不如概率性isprimes快),但我的生成器是非概率性的,因此速度很快。生成这些质数只需要0.011779069900512695秒。它位于此处:https://github.com/oppressionslayer/primalitytest,并使用for循环和lars_next_prime函数。 - oppressionslayer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接