【问题标题】:Most efficient way to filter prime numbers from a list of random numbers in Python从 Python 中的随机数列表中过滤素数的最有效方法
【发布时间】:2017-12-05 21:43:10
【问题描述】:

我有一个充满随机数的列表,我想从这个列表中返回素数。所以我创建了这些函数:

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

    return number > 1

def filter_primes(general_list):
    return set(filter(is_prime, general_list))

但是我想提高性能,那我该如何实现呢?

【问题讨论】:

  • 那里有 的解决方案,特别是在 SO 上显示素数检查的实现。你检查过其中任何一个吗?
  • 不确定这是否有助于提高性能,因为我没有时间测试任何东西,但这个 return True if number > 1 else False 可以简化为:return number > 1
  • 是的,有很多质数检查的解决方案,但没有一个适用于随机数列表的场景。
  • @flpn:一个数字不会因为它在一个列表中而突然变得更少质数。
  • 最好的,IMO,你能做的就是使用 Miller Rabin 作为主要测试算法。

标签: python algorithm performance python-3.x primes


【解决方案1】:

这个怎么样?我觉得还好一点:

def filter_primes(general_list):
   return filter(is_prime, set(general_list))

这样我们就不会多次呼叫is_prime() 同一个号码。

【讨论】:

    【解决方案2】:
    1. Eratosthenes 筛比您使用的方法 Trial Division 更有效。

    2. 您的试用除法循环可以提高效率,只需大约一半的时间。 2 是唯一的偶素数,所以把 2 当作特例,之后只处理奇数,这样工作量就减半了。

    我的 Python 不存在,但这个伪代码应该让事情变得清晰:

    def isPrime(num)
    
      // Low numbers.
      if (num <= 1)
        return false
      end if
    
      // Even numbers
      if (num % 2 == 0)
        return (num == 2)  // 2 is the only even prime.
      end if
    
      // Odd numbers
      for (i = 3 to sqrt(num) + 1 step 2)
        if (num % i == 0)
          return false
        end if
      end for
    
      // If we reach here, num is prime.
      return true;
    
    end def
    

    for 循环中的 step 2 使工作减半。之前消除了所有偶数,您只需要使用奇数除数进行测试:3、5、7、...

    【讨论】:

    • 它不是筛子,它只是测试提供的数字。如果您将相同的数字传递两次,它将对该数字进行两次测试。筛子将计算一个范围内的数字,并在询问时返回预先计算的结果。
    【解决方案3】:

    Sieve of Eratosthenes,在我的设备上的 PyPy 3.5 上,1000 万以下的质数大约需要 0.17 秒:

    from array import array
    
    def primes(upper):
        numbers = array('B', [1]) * (upper + 1)
    
        for i in range(2, int(upper ** 0.5) + 1):
            if numbers[i]:
                low_multiple = i * i
                numbers[low_multiple:upper + 1:i] = array('B', [0]) * ((upper - low_multiple) // i + 1)
    
        return {i for i, x in enumerate(numbers) if i >= 2 and x}
    

    和过滤功能:

    filter_primes = primes(10_000_000).intersection
    

    【讨论】:

    • 一旦i 到达sqrt(upper),你就识别出了所有的素数。
    • @flpn:不,但如果你所有的随机数都在 0 到 10,000,000 之间,那么关键是你可以获取该范围内的所有素数并检查每个随机数是否在该集合中。这就是 filter_primes = primes(10_000_000).intersection 的作用——像往常一样调用 filter_primes(general_list)
    【解决方案4】:

    3 轮 Miller-Rabin 测试 (https://en.wikipedia.org/wiki/Miller%2dRabin_primality_test) 使用基数 2、7 和 61,可以准确检测所有 int 的素数。

    如果数量很大,这比试除法或筛分要快得多。

    如果数字不能很大(即,如您在 cmets 中建议的那样,

    【讨论】:

      【解决方案5】:
      def primes_list(num_list):
          divs = [2,3,5,7]
          primes = [x for x in set(num_list) if 0 not in {1 if ((x%i != 0) | (x in divs)) & (x > 0) else 0 for i in divs}]
          return primes
      

      对于此函数,它采用列表num_list 作为参数。 divs 是一个预定义的,或者更确切地说是硬编码的,小于 10 的素数列表,不包括 1。然后我们使用列表推导来过滤 num_list 的素数作为变量 primes

      【讨论】:

        猜你喜欢
        • 2010-09-27
        • 2011-06-05
        • 2010-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-07
        • 2019-09-19
        • 2013-01-19
        相关资源
        最近更新 更多