【问题标题】:Removing non-primes numbers from list从列表中删除非素数
【发布时间】:2011-10-16 19:01:22
【问题描述】:

我有这样的东西:

palindromes=[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 101, 111, 121, ..., 99799, 99899, 99999]
# of course im generating it :)

def isPrime(number):
    for i in range(2,int(number/2)+1):
        if number%i == 0:
            return True
    return False

def removeNonPrimes(palindromes):
    for palindrom in palindromes:
        if isPrime(palindrom):
            palindromes.remove(palindrom)
    return palindromes

palindromes = removeNonPrimes(palindromes)

它不会删除所有非素数

我不知道为什么

【问题讨论】:

  • 您的函数 isPrime 应命名为 isNonPrime 以使其与其返回值一致。这个 SO question 有更多关于在迭代 stackoverflow.com/questions/1207406/… 时从列表中删除项目
  • 如果你想找出所有的回文素数,最好先找到素数,然后检查哪些是回文数。
  • @hughdbrown n 下面有多少回文?说n = 10^m,然后numPAL(n) = SUM 10^(ceiling(i/2)) {i=1,2..m} = 2*SUM 10^j {j=1,2..m/2} 让我们说这是关于2*sqrt(n)。由于对k 以下数字的试除法测试是k^1.5 给定或取一个对数因子,因此对回文的素性整体测试将约为~ 3*n^0.75。但是由埃拉托色尼的筛子生成低于n 的素数需要~ n*log(log(n)) 时间。哪个更糟。

标签: python primes


【解决方案1】:

在您的代码中:

def removeNonPrimes(palindromes):
    for palindrom in palindromes:
        if isPrime(palindrom):
            palindromes.remove(palindrom)
    return palindromes

您正在修改(使用.remove())您正在迭代的同一个列表(使用in)。不建议这样做,您最终可能会删除您想要的不同项目。

相反,考虑一个列表推导

def removeNonPrimes(palindromes):
    return [p for p in palindromes if isPrime(p)]

【讨论】:

  • 谢谢 :) 现在可以了,但是..为什么?从逻辑上讲,我认为这很好:)
  • 我的回答解释了为什么在 for 循环中使用 remove 不起作用。 Python 不同意你的观点。
  • 在原始版本中,您正在修改一个列表,同时循环它,这意味着 Python 开始查看错误的项目。在列表推导中,原始列表保持不变,而是创建了一个新列表。
【解决方案2】:

isPrime 的返回值与应有的相反。

【讨论】:

  • 但是removeNonPrimes中的逻辑也是相反的。所以两个负数使一个正数:)。
【解决方案3】:

您正在迭代一个可迭代对象并同时修改该可迭代对象。因此,当一个循环完成时,指针可能不一定指向您正在迭代的原始集合中的下一项。

如果 isPrime(palindrom) 返回 True 并且您删除了一项,则列表的长度会减一。

考虑回文 = [7,8,9,10] 且回文的值为 8 的情况。 palindrom 实际上指向回文[1]。循环结束后,回文将指向回文[2]。 同时由于 8 将被从回文中删除,因为它不是素数。 现在 palindromes = [7,9,10] 和 palindrom = palindromes[2] = 10。您可以看到值 9 从未被触及。

道德:永远不要操纵你正在迭代的可迭代对象。这就像砍掉你坐的那棵树的树枝。

(顺便说一句,函数名应该是 isNotPrime 而不是 isPrime 以反映它正在检查的内容。:))

【讨论】:

    【解决方案4】:

    如果你想要所有回文素数,这会更好:

    def eras(n):
        last = n + 1
        sieve = [0,0] + list(range(2,last))
        sqn = int(round(n ** 0.5))
        it = (i for i in xrange(2, sqn + 1) if sieve[i])
        for i in it:
            sieve[i*i:last:i] = [0] * (n//i - i + 1)
        return filter(None, sieve)
    
    def is_palindrome(n):
        ns = str(n)
        ns_reversed = ''.join(reversed(ns))
        return ns == ns_reversed
    
    if __name__ == '__main__':
        primes = eras(100 * 1000)
        print [p for p in primes if is_palindrome(p)]
    

    【讨论】:

    • 您是否尝试过为这两种变体计时?当然问题的isPrime函数必须改为停在sqrt(n),而不是n/2。请注意,OP 说他“生成”了回文,而不是为它们测试所有自然数。那部分不见了。
    • 不,我没有对变体计时。那不是我的意思。但既然你问:在大小为 1 000 000 的测试中,OP 的代码需要 3.250 秒。我的代码需要 0.138 秒。不,我没有优化 OP 的代码。我的观点是素数的数量远小于回文数的数量,因此首先计算素数并测试它们是否是回文将是更好的方法。
    • 嗯。我必须修改这个评论:回文数的数量小于素数的数量。我的代码更快,因为素数计算(整体)优于 OP 的素数测试代码(分段)。
    • 只需将 OP 代码中的 sqrt(n) 替换为 n/2 即可大大加快速度。它会变成~ n^1.5 左右,而不是~ n^2
    • 我将它用于主要测试:def isPrime(number): return not any(number % i == 0 for i in range(2, int(number ** 0.5) + 1)) 我们是否同意这是一种改进?在 1 000 000 上:OP 为 0.900 秒,我的代码为 0.136 秒。
    猜你喜欢
    • 1970-01-01
    • 2015-09-14
    • 2017-03-05
    • 2021-03-17
    • 2022-08-14
    相关资源
    最近更新 更多