python判断孪生质数对(素数对)并计算个数。

很久前在知乎写的一个答案,今天把坑填了,顺便搬过来。

让我们定义dn为:dn=pn+1−pn,其中pi是第i个素数。显然有d1=1,且对于n>1有dn是偶>>数。
“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。 现给定任意正整数N(<10^5), 请计算不超过N的满足猜想的素数对的个数。

而且题目还限制了400ms时间(有没有搞错(╯‵□′)╯︵┻━┻)

写出的第一个程序在最大值情况下运行时间超过10秒 (;′⌒`)

后来在网上看到了一个人的回答,使用到了初中时学到的数学知识:

假如一个数N是合数,它有一个约数a,那么有a×b=N
则a、b两个数中必有一个大于或等于根号N,一个小于或等于根号N。
因此,只要小于或等于根号N的数(1除外)不能整除N,则N一定是素数。
原谅我数学没学好,依照这个思路 我写了下面这个程序:

    import math   #使用 math 模块来开平方
    import time   #计算程序运行时间
    num = int(input())    #输入一个整数
    s = 0                 #计算质数对的个数
    start = time.time()    #计算开始时间
    
    
    def f1(n):
        if n <= 1:
            return False
        else:
            for i in range(2, int(math.sqrt(n)) + 1):   #这里 +1是将开方后的结果包含在内
                    if n % i == 0:
                        return False              
            return True           
    
    
    for i in range(1, num+1):
        if f1(i) is True and f1(i + 2) is True and (i + 2) <= num: #最后的条件是限制素数对超过N
            s += 1
    end = time.time()           #结束时间
    print(s)                    #打印素数对个数
    print(end - start)          #输出运行时间

嗯 这就是大概的逻辑,最后时间大大缩短,用100000来实验,时间是0.4561。略微超过了400ms。

怎么办…… 于是继续找,总有人跟我遇到一样的问题嘛?

最后还真找到了!

要想继续优化缩短运行时间,就是按照上面的思路来排除多余的工作量。既然我们已经排除了一半,那是不是还能排除跟多呢?

废话!当然可以啦~(不然也不会写到这里)

首先想到的就是偶数! 除了2以外,显然偶数不是素数。好的,又排除了一半。

顺着思路,那么奇数里面能被3,被5整除的也要排除。

顺着这个思路 我们得到这个代码:

import math     # 使用 math 模块来开平方
import time     # 计算程序运行时间
num = int(input())    # 输入一个整数
s = 0                 # 计算质数对的个数
start = time.time()    # 计算开始时间


def f1(n):
    if n <= 1:
        return False
    elif n % 2 == 0 and n != 2:     # 去除能被2整除的  不包括2(其实也可以包括2,以为2没有素数对)
        return False
    elif n % 3 == 0 and n != 3:     # 去除能被3整除的  不包括3
        return False
    elif n % 5 == 0 and n != 5:     # 去除能被5整除的  不包括5
        return False
    elif n % 7 == 0 and n != 7:     # 去除能被7整除的  不包括7
        return False
    else:
        for i in range(3, int(math.sqrt(n)) + 1, 2):   # 这里 +1是将开方后的结果包含在内
                if n % i == 0:
                    return False
        return True


for i in range(1, num+1):
    if f1(i) is True and f1(i + 2) is True and (i + 2) <= num:  # 最后的条件是限制素数对超过N
        s += 1
end = time.time()
print(s)
print(end - start)

用 100000 来实验,得到的时间是207ms!!! 合格啦!!! 少了一半多! 但是能不能更少呢?

还真能!

要想再简化,就不得不了解孪生质数的性质和分布规律。

一. 当n≥5时,如果一个n-1,n+1互为孪生质数,则n必定是6的倍数

  • 简要证明:因为:n - 1,n + 1是质数
    • 故 :n - 1 ,n + 1是奇数
    • 可知:n 是 偶数,n 是 2 的倍数。
    • 假设:n 不是 3 的倍数,即 n = 3k + 1 或 n = 3k + 2。
      • (a) 当 n = 3k + 1 时,那么 n - 1 = 3k,已经与 n - 1 是质数矛盾。
      • (b) 当 n = 3k + 2 时, 那么 n +1=3(k + 1),已经与 n + 1是质数矛盾。
      • 故:n 是 3 的倍数。
      • 因为:n既是2的倍数,又是3的倍数
      • 故:n 是6的倍数。

得出:
推论一:当 x >= 1, (6x - 1)或 (6x + 1)不是质数时,即
2(3x) - 1, 3(2x) - 1,
2(3x) + 1, 3(2x) + 1,为合数时,
它们的质因子不包括2和3的倍数。(合数为可分解为多个质数的乘积)

二. 对于大于3的质数,只分布在6x-1和6x+1两数列中(x为非0自然数)。即都在6的倍数的两侧。

也就是说,大于5的质数一定在6x两侧,但是6x的两侧不一定是质数。

6x-1数列中的素数为阴性素数,合数为阴性合数
6x+1数列中的素数为阳性素数,合数为阳性合数

好了,依照之前的和以上两个性质的思路。

可知,对于大于3的孪生质数

  1. 5+6n数列中,素数必定为阴性素数,且合数也不能被2,3整除,所以是否能被2,3整除不用判断
  2. 只需满足5+6n和7+6n同时为素数,即都不能被5或7 整除即可。

代码如下:

num = 100000
s = 1    # (3,5)质数对单独处理,不再考虑

def f(n):
    if n % 5 != 0 and n % 7 != 0:    # 判断一个属于是否能被5或7整除
        return True    # 真,则为质数
    else:
        return False


for n in range(5,num+1,6):  # 遍历5+6n数列
    if n == 5:    # (5,7)单独处理
        s += 1
    elif f(n) and f(n+2):   # 是否同时为素数
        s += 1
print(s)

最终用时为 **7ms!!!**比原来整整少了几个数量级。

这个是从判断是否为孪生素数对的角度来寻找思路,对于是否为素数,我想也没必要赘述了。按照以上思路写就可以了。

猜你喜欢

转载自blog.csdn.net/ICERON/article/details/84996617