【问题标题】:Goldbach's Conjecture - Find the number of ways an even number can be written as the sum of two primes哥德巴赫猜想 - 找出偶数可以写成两个素数之和的方式数
【发布时间】:2016-12-31 18:14:58
【问题描述】:

我想知道给定的正偶数可以写成两个素数之和的方式数。

目前,我有这个代码:

n = int(input(">"))
def genprimes(n):#generate primes from 2 to n
    primes = []
    for i in range(2,n):
        prime = True
        for a in range(2,i):
            if i % a == 0:
                prime = False
        if prime == True:
            primes.append(i)
    return primes

pairs = []
primes = genprimes(n)

for prime1 in primes:
    for prime2 in primes:
        if prime1 + prime2 == n and [prime1,prime2] not in pairs and [prime2,prime1] not in pairs:
            pairs.append([prime1,prime2])
print(len(pairs))

它可以工作,但是当n 超过10,000 ish 时它会变得有点慢。谁能想出一种更有效的方法来找到这个值,以便它为高值产生快速的结果?

【问题讨论】:

    标签: python math


    【解决方案1】:

    你有两个慢算法。

    要获取素数列表,您需要分别检查每个数字的素数。获得素数列表的最快方法是使用预先构建的素数列表——这就是我为此类问题所做的。最多 2**16 (65,536) 的素数列表只需要 6542 个整数,我将该列表存储在一个模块中。您可能更喜欢使用Sieve of Eratosthenes,这通常被认为是从头开始获取此类列表的最快方式。

    然后您尝试每对素数,看看它们是否添加到您的目标数中。对于每个素数p,查看n-p 是否也是素数会快得多。正如@Shubham 所建议的那样,您可以使用二进制搜索检查n-p,但是拥有两个索引可能会更快,一个定期上升并产生p,另一个下降根据需要检查n-p。将您的主要列表复制到一组并使用它来检查n-p 是否在列表中可能是最快的。最后两种技术顺序为n,但对于仅高达 10,000 的数字,开销可能会影响哪种技术最好。是的,对于此类问题,10,000 并不是很大。最后,如果内存不是问题,@BoulderKeith 指出您可以将素数列表保留为布尔值列表(真/假)并非常快速地检查 n-p 是否为素数 - 如果大多数内存,这是最快的- 消耗,技术。

    【讨论】:

    【解决方案2】:

    这可以通过优化程序的两个部分来有效地完成。

    首先,我们可以通过使用埃拉托色尼筛生成素数来优化genprimes(),它可以在O(nlog(log(n)) 中生成高达n 的素数。

    之后,一旦我们有了素数列表,我们就可以执行以下操作:

    • 遍历素数列表并选择一个素数,例如p1
    • 使用二分搜索检查n-p1 是否也存在于素数列表中。

    如果p = number of primes till n

    那么这部分的复杂度将是O(plog(p))

    正如@PaulHankin 建议的那样,从@SrujanKumarGulla 的回答中,一旦我们有了主要列表,我们就可以建立一个O(p) 解决方案。

    第二部分的实现(第一部分是标准的Sieve of Eratosthenes):

    # prime list is assumed to be sorted. It will be if it's generated
    # using Sieve of Eratosthenes
    
    def solutions(n, prime_list):
        low = 0, high = len(prime_list)-1
        sol = []
        while low < high:
            temp = prime_list[low] + prime_list[high]
            if temp == n:
                sol.append((prime_list[low], prime_list[high], ))
            elif temp < n:
                low += 1
            else:
                high -= 1
    
        return len(sol)
    

    【讨论】:

    • 给定(排序的)素数列表 stackoverflow.com/questions/1494130/… 的回答。跨度>
    • @PaulHankin 我检查了答案,但它指出,一旦n = 索引ij 的值之和,我们应该返回。它不会只找到一种可能性而不是所有可能性吗?我想建议使用bitmap(也可以是O(p))的解决方案,但它只有在最大数量在10^7 附近时才有效。在我看来,HasMap 解决方案应该是最好的。
    • 是的,该解决方案只提供了一对,但更改它以生成所有解决方案几乎是微不足道的——只需将“返回”替换为“附加到解决方案列表”即可。
    • @PaulHankin 哦,是的。抱歉,我错过了。
    【解决方案3】:

    如果内存不是问题,您可以通过以下方式进一步优化速度:

    1) 如果 i 是素数,则使用 array[i]=true 从 2..n 构建一个数组。 (可以使用 Eratosthenes 的筛子或更高级的东西。)

    2) 对于所有 2

    整个算法是O(n log n)。 (O(n log n) 构建素数;O(n) 找到对。)

    我在 C# 而不是 Python 中执行此操作,但能够在 10 秒内找到 n = 100,000,000 的所有对。

    【讨论】:

    • 我忘记了将 Eratosthenes 的筛子保留为布尔值或等效项的列表——这将是最快的。做得好!我赞成这个答案,如果你不介意,我会在我的答案中提到这种技术。 (我会等半个小时等你的回复。)
    • 抱歉延迟回复。请随意使用我的回复。
    【解决方案4】:
        def prim(n):
            primes = []
            for num in range(1, n + 1):     # current number check
                limit = int(num ** 0.5) + 1 # search limit
                for div in range(2, limit): # searching for divider
                    if (num % div) == 0:    # divider found
                        break               # exit the inner loop
                else:                       # out of the inner loop
                    primes.append(num)      # no divider found hence prime
            return(primes)
    
        num = int(input('Input an even number: '))
        primes = prim(num)                  # create list of primes
    
        for i in range(num//2 + 1):
            if num % 2 != 0:
                print(num, 'not even'); break
            if i in primes and (num - i) in primes:
                print(i, '+', num - i)
    """
    Examples:
    =========
    Input an even number: 18
    1 + 17
    5 + 13
    7 + 11
    
    Input an even number: 88
    5 + 83
    17 + 71
    29 + 59
    41 + 47
    
    Input an even number: 17
    17 not even
    """
    

    【讨论】:

    • 为你的答案添加一些解释
    【解决方案5】:

    这是来自Rory 建议的实际解决方案,它使用埃拉托色尼筛高效地生成素数,然后查看n-prime 是否也是素数。

    满足这一点的解决方案的数量需要减半并向上取整以防止重复。

    n = int(input(">"))
    def genprimes(n):
        limitn = n+1
        not_prime = set()
        primes = []
    
        for i in range(2, limitn):
            if i in not_prime:
                continue
            for f in range(i*2, limitn, i):
                not_prime.add(f)
            primes.append(i)
        return primes
    
    nos=0
    primes = genprimes(n)
    
    for prime in primes:
        if n-prime in primes:
            nos += 1
    print(str(int((nos/2)+0.5)))
    

    【讨论】:

    • 您的检查if n-prime in primesprimes 列表中的线性速度,因此您的最后一部分是二次方的,可能不必要地慢。将primes 复制到一组可能会加快速度。有关加快该部分代码的其他想法,请参阅我的答案中最近添加的内容。
    【解决方案6】:

    使用此版本的埃拉托色尼筛法,可以快速 找出可以写出偶数的方法的数量 作为两个素数之和:

    import numpy as np
    def goldbach(n):
        P5m6 =np.ones((n//6+1), dtype=bool)
        P1m6 =np.ones((n//6+1), dtype=bool)
        for i in range(1,int((n**0.5+1)/6)+1):
            if P5m6[i]:
                P5m6[6*i*i::6*i-1]=[False]
                P1m6[6*i*i-2*i::6*i-1]=[False]
            if P1m6[i]:
                P5m6[6*i*i::6*i+1]=[False]
                P1m6[6*i*i+2*i::6*i+1]=[False]
    
        nmod6=n%6
        if nmod6==0:
            return np.count_nonzero(P5m6[1:n//6]&P1m6[n//6-1:0:-1])
        elif nmod6==2:
            return np.count_nonzero(P1m6[1:(n-6)//12+(n//6-1)%2+1]&P1m6[n//6-1:(n-6)//12:-1])+np.count_nonzero(P5m6[n//6])
        elif nmod6==4:
            return np.count_nonzero(P5m6[1:n//12+(n//6)%2+1]&P5m6[n//6:n//12:-1])+np.count_nonzero(P1m6[n//6])
        else:
            return -1
    print(goldbach(100000000))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多