0

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

例如,在 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
4

3 回答 3

3

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

于 2016-03-17T02:34:58.937 回答
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 编写)。

于 2016-03-17T02:50:43.537 回答
1

我根据 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
于 2016-03-17T02:39:07.517 回答