【问题标题】:prime sieve returning some strange numbers素筛返回一些奇怪的数字
【发布时间】:2016-03-17 02:00:17
【问题描述】:

我正在尝试开发一个素数筛,在纸面上,我的算法在纸面上非常有意义,但会在平方根上方的素数中返回一个非常短的合数选择。

例如,在 10,000(其平方根为 100)的限制(查找所有素数)的情况下,它与素数混合的合数是 115、119、121 和 125 (都非常接近(及以上!)100)。

请让我知道我的代码有什么问题,哪些部分需要修复/如何修复。

澄清:我担心它返回的复合(非素数),我在素数测试中哪里出错了,我该如何纠正它?

到目前为止,这是我的筛子:

def primes(limit):
    # Just make an empty list where the primes go
    prime = []
    # This next for loop just adds all the numbers of the form 6n+/-1 to the list, as all primes are of this form
    for i in range(1,limit / 6 + 1):
        prime.append(6*i - 1)
        prime.append(6*i + 1)
    # If the limit is divisible by 6, the last number on the list is sometimes over the limit
    if limit % 6 == 0 and prime[-1] > limit:
        prime.remove(prime[-1])
    # This next line just finds the place of the 'square root' of the limit, which is as high as it has to check for factors
    squareroot = min(range(len(prime)), key=lambda i: abs(prime[i]-(limit**0.5))) + 1
    # Removing composites BELOW the square root
    for p in prime[:squareroot][:]:
        for f in range(2, int(p ** 0.5) + 1):
            if p % f == 0:
                prime.remove(p)
                break
    # Removing composites ABOVE the square root
    for f in prime[:squareroot][:]:
        for p in prime[squareroot:]:
            if p % f == 0:
                prime.remove(p)
    return [2, 3] + prime

【问题讨论】:

  • “缩进仅在第一行有问题”。选择第一行下面的所有行并单击{} 代码按钮比键入该句子要快。
  • 我不明白你的实际问题:你是担心你得到超过 100 的值,还是担心你得到合数?
  • 所有素数都不是6n +/- 1的形式。
  • Peter wood -- 是的,至少是 2 和 3 以上的素数。这已经在数学上得到证明。至于 Evert,我担心我会得到复合数字(如 115、119 等)——但我得到的所有复合数字都正好高于极限的平方根(在本例中为 10,000)。我想知道为什么我会得到这些复合材料,以及如何才能停止得到它们。
  • 但是您的素数列表不包括 23

标签: python primes sieve


【解决方案1】:

一旦删除平方根以下的素数,就不能再使用squareroot 作为primes 的索引,因为primes 的长度将会改变。

【讨论】:

    【解决方案2】:

    您获得高于平方根的复合材料的一个原因是您的循环是如何构建的。当从列表中删除一个项目时,所有位于较高索引的项目都向下移动一个。因此,当在第一个循环中删除一个项目时,平方根会向下移动。当第二个循环开始时,squareroot 不再是平方根的索引。

    # Removing composites BELOW the square root
    for p in prime[:squareroot][:]:
        for f in range(2, int(p ** 0.5) + 1):
            if p % f == 0:
                prime.remove(p)  # <- you removed an item from `prime`, so the
                break            # square root is now at prime[squareroot - 1]
    
    # Removing composites ABOVE the square root
    for f in prime[:squareroot][:]:    #    now p[squareroot] is actually a number
        for p in prime[squareroot:]:   # <- above the real square root, so this 
            if p % f == 0:             #    loop starts too high
                prime.remove(p)
    

    修复它的一种方法是在第一个循环中删除一个值时调整squareroot 的值。另一种方法是在第二个循环之前重新计算squareroot

    在迭代列表时从列表中添加或删除项目通常是一个坏主意。例如,可以在一次传递中标记项目(例如将它们设置为零或无),然后可以在第二次传递中复制未标记的项目。

    编辑添加示例代码来标记复合:

    # Removing composites BELOW the square root
    for i,p in enumerate(prime[:squareroot]):
        for f in range(2, int(p ** 0.5) + 1):
            if p % f == 0:
                prime[i] = 0  # <- mark composites
                break         
    
    # Removing composites ABOVE the square root
    for f in prime[:squareroot]:    
        if f == 0: 
            continue                              # skip composites
        for i,p in enumerate(prime[squareroot:]): # <- this loop is okay now
            if p % f == 0:            
                prime[i] = 0                      # mark composite
    
    # at this point, prime contains 0's where the compsites were found
    # and non-zeros for the primes.  Just need to collect all the
    # non-zero elements.
    result = []         
    for p in prime:     
        if p:                    
            result.append(p)
    

    您的代码还有其他问题,但这应该可以回答您的直接问题。随着你对 python 的熟练程度越来越高,你会看到你可以做出进一步的改进(一个初筛可以用大约 6 行 python 编写)。

    【讨论】:

    • 谢谢 - 我听说在迭代时删除项目会导致麻烦,但真的有办法避免这种情况吗?
    • 添加了在迭代 prime 时不会删除项目的示例代码。
    【解决方案3】:

    我根据 T. Silver 的回应修复了它——我只是做了它,以便在确保低于极限平方根的所有内容都是素数之后,它会再次找到平方根。这是固定的代码:

    # Just make an empty list where the primes go
    prime = []
    # This next for loop just adds all the numbers of the form 6n+/-1 to the list, as all primes are of this form
    for i in range(1,limit / 6 + 1):
        prime.append(6*i - 1)
        prime.append(6*i + 1)
    # If the limit is divisible by 6, the last number on the list is sometimes over the limit
    if limit % 6 == 0 and prime[-1] > limit:
        prime.remove(prime[-1])
    # This next line just finds the place of the 'square root' of the limit, which is as high as it has to check for factors
    squareroot = min(range(len(prime)), key=lambda i: abs(prime[i]-(limit**0.5))) + 1
    # Removing composites BELOW the square root
    for p in prime[:squareroot][:]:
        for f in range(2, int(p ** 0.5) + 1):
            if p % f == 0:
                prime.remove(p)
                break
    # Here's where i put the fix!
    squareroot = min(range(len(prime)), key=lambda i: abs(prime[i]-(limit**0.5))) + 1
    # Removing composites ABOVE the square root
    for f in prime[:squareroot][:]:
        for p in prime[squareroot:]:
            if p % f == 0:
                prime.remove(p)
    return [2, 3] + prime
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-20
      相关资源
      最近更新 更多