【问题标题】:Finding the largest prime number "within" a number找到一个数字“内”的最大素数
【发布时间】:2014-01-16 02:41:20
【问题描述】:

对于这个问题,我需要在一个较大的数字中找到最大的质数。出于示例的目的,假设较大的数字是“123456789”,那么我必须检查的一些数字是 12、456、234567 等。

我写了一些 Python 代码来解决这个问题,但是对于我要检查的数字,它运行得非常慢。我正在使用的实际数字约为 10000 位,因此我需要查看很多数字。这是我的代码:

num = "123456789"

def isPrime(n):
# 0 and 1 are not primes
if n < 2:
    return False
# 2 is the only even prime number
if n == 2:
    return True
# all other even numbers are not primes
if not n & 1:
    return False
# range starts with 3 and only needs to go up the squareroot of n
# for all odd numbers
for x in range(3, long(n**0.5)+1, 2):
    if n % x == 0:
        return False
return True

def largestPrime():
    largest = 2
    for i in range(0,len(num)):
        for j in range(i+1,len(num)):
            if isPrime(long(num[i:j])):
                if long(num[i:j]) > largest:
                    largest =long(num[i:j])
    print largest

def main():
    largestPrime()

main()

我很确定这段代码给出了正确的答案,但正如我所说,它真的很慢。谁能帮我弄清楚如何加快速度?

感谢您的帮助!

【问题讨论】:

  • 一个明显的改进:将if isPrime...if &gt; largest 重新排列为if &gt;largest...if isPrime,因此您只检查它是否是质数,如果它大于您迄今为止找到的最大质数(如果它更小,您不会'不在乎它是否是素数)。
  • 有许多主要测试算法比您编写的幼稚算法更有效。特别是如果你可以接受误报——你可以;您所要做的就是保留最大的“可能是素数”数字的排序列表,然后最后对最大的数字进行缓慢但保证正确的测试。当然,您需要做一些研究来选择最适合您需求的算法。
  • 您的主要检测算法是主要瓶颈。仅用他的算法找出整个 10000 位数字是否是素数就需要一段时间。查看en.wikipedia.org/wiki/Primality_test 以获得更好的测试。
  • 附带说明,以largest=2 开头是一个非常小的优化,考虑到这意味着您可能会得到不正确的值(例如,在100000000000 上尝试...),我不知道觉得值得。
  • 最后,在一堆算术上做一个紧密的循环正是 Python 不擅长的。如果可能的话,您想将内部循环(isPrime)推入 C 中。通过在 Cython 中编写它很容易做到这一点。或者,有一些巧妙的方法可以将大量数字的算术转换为 numpy 数组的矢量化算术。或者只是在 PyPy 而不是 CPython 中运行您的代码——有时(并且每个版本更常见)它会设法将您的代码 JIT 到合理的速度。但是,在您获得 ZPP 或类似的易于处理的算法之前,请不要担心这一点。

标签: python performance optimization


【解决方案1】:

我可能会使用从总位数开始并查看它是否为素数的策略。然后继续将数字减一,同时向左移动以查看是否为素数。让我用一个例子来解释:

123456789

First check the 9-digit number: 123456789
Then check the 8-digit numbers: 23456789, 12345678
Then Check the 7-digit numbers: 3456789, 2345678, 1234567
etc.

【讨论】:

    【解决方案2】:

    我看到的一个问题是,对于一些大数字,您将多次测试相同的数字。例如对于“123456712345671234567”,您的代码将测试“1234567”3 次。我建议您制作一个不包含重复项的集合,然后对每个数字进行主要测试。我也认为对数字进行排序是个好主意,因为我们可以在找到第一个素数后停止。

    接下来,如果您要处理大量数字(例如 10000 位),我建议您使用统计素数检验。下面我使用来自wikipedia 的伪代码进行了 Miller-Rabin 素数测试。

    我已经重写了你的代码:P

    import random
    num = '3456647867843652345683947582397589235623896514759283590867843652345683947582397589235623896514759283590784235876867843652345683947582397589235623896514759283590784235876867843652345683947582397589235623896514759283590784235876867843652345683947582397589235623896514759283590784235876867843652345683947582397589235623896514759283590784235876867843652345683947582397589235623896514759283590784235876867843652345683947582397589235623896514759283590784235876784235876324650'
    
    def probablyPrime(num, k):
        """Using Miller-Rabin primality test"""
        if num == 2 or num == 3:
            return True
        if num < 2:
            return False
        if not num & 1:
            return False
    
        # find s and d such that n−1 = (2**s)*d with d odd
        d = (num-1) >> 1
        s = 1
        while not (d & 1):
            d = d >> 1
            s += 1
    
        # run k times
        for _ in range(k):
            a = random.randint(2, num-2)
            x = pow(a, d, num)  # more efficient than  x = a**d % num
            if not (x == 1 or x == num-1):
                for _ in range(s-1):
                    x = (x**2) % num
                    if x == 1:
                        return False
                    if x == num-1:
                        break
                if not x == num-1:
                    return False
        return True
    
    
    def largestPrime(num):
        num_list = set([])
        for i in range(0,len(num)+1):
            for j in range(i+1,len(num)+1): 
                inum = int(num[i:j])
                # Don't append numbers that have already appeared
                if inum not in num_list:
                    num_list.add(inum)
    
        # Convert to list and sort
        num_list = list(num_list)
        num_list.sort(reverse=True)
    
        for num in num_list:
            print('Checking ' + str(num))
            if probablyPrime(num,100):
                print('\n' + str(num) + ' is probably the largest prime!')
                return
    
    largestPrime(num)
    

    另一种提高速度的方法可能是 python 的multiprocessing 包。

    【讨论】:

    • 这个想法很好,但是列表对于这个问题来说是一个非常糟糕的数据结构。使用一组。
    • 我同意 Voo 的观点。我更新了我的代码以使用 set。我已经用一些大数字测试了这个脚本,真正的减速似乎是当 isPrime() 处理 50+ 位数字时。我会考虑一个更好的主要测试,但即使是一个非常好的测试,我敢打赌一个 10000 位的数字也需要很长时间。
    【解决方案3】:

    代码:

    def isprime(n):
        if n == 2:
            return str(n)+" is the biggest prime"
        if n % 2 == 0:
            return isprime(n-1) #not prime, check again for next biggest number
        max = n**0.5+1
        i = 3
        while i <= max:
            if n % i == 0:
                return isprime(n-1) #not prime, check again for next biggest number
            i+=2
        return str(n)+" is the biggest prime"
    
    print "Testing 7:",isprime(7)
    print "Testing 23:",isprime(23)
    print "Testing 2245:",isprime(2245)
    print "Testing 222457:",isprime(222457)
    print "Testing 727245628:",isprime(727245628)
    

    输出:

    >>> 
    Testing 7: 7 is the biggest prime
    Testing 23: 23 is the biggest prime
    Testing 2245: 2243 is the biggest prime
    Testing 222457: 222437 is the biggest prime
    Testing 727245628: 727245613 is the biggest prime
    

    【讨论】:

      猜你喜欢
      • 2014-11-28
      • 1970-01-01
      • 2021-05-12
      • 2022-11-13
      • 2014-08-01
      • 2022-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多