PTA(Basic Level) 1007 素数对猜想 (20分)

python 1007 素数对猜想 (20分)

PAT (Basic Level) Practice(中文) 1007 素数对猜想 (20分)

让我们定义 dn 为: dn= pn+1pn ,其中 pi 是第iii个素数。显然有 d1=1,且对于n>1n>1n>1 dn是偶数。“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。

现给定任意正整数N105,请计算不超过N的满足猜想的素数对的个数。

输入格式:

输入在一行给出正整数N

输出格式:

在一行中输出不超过N的满足猜想的素数对的个数。

输入样例:

20

输出样例:

4

测试点分析:

测试点0不知道应该就是送分的
测试点1是5
测试点2是可能是2,3,4输出是0
测试点3是6的倍数,且如果判断范围+1了就会多出来一对例如n=12
测试点4是一个平方数,所以判断上限要+1
测试点5是100000

实现代码:

n=int(input())
a=[]
t=[1]*(n+2)  #建立初始素数判断表,初始所有数字均为素数
#因为p在循环后还会p+=1,且index+1=n,因此该表为n+2的表
p=2  #指向素数的编辑
c=0
while p<=n:
    a.append(p)  #将改数字加入素数列表
    for i in range(2*p,n+1,p):#从该素数的2倍开始将该素数的所有倍数的素数布尔值改为假
        t[i]=0
    while True: #p不断加1,直到发现素数表内布尔值为真的数,跳出循环
        p+=1
        if t[p]==1:break
for i in range(len(a)-1):  #在素数列表中判断是否符合素数对猜想
    if a[i]+2==a[i+1]:
        c+=1
print(c)
耗时:

测试点5需要99ms
在这里插入图片描述

代码优化:

import math
def pi(n):#判断是否为素数,原因见总结
    x,y=n-1,n+1
    for i in range(5,int(math.sqrt(y)+1),6):
        t=i+2
        if x%i==0 or x%t==0 or y%i==0 or y%t==0:return False
    return True
n=int(input())
c=1 if n>4 else 0  #此方法只能判断大于3的素数,因此n>5时,3,5为构成一对
for i in range(6,n,6):  #传入6两侧的数字
    if pi(i):
        c+=1
print(c)
耗时:

测试点5只需要43ms
测试点5耗时43ms

总结:

这道题难点主要在于用的是 Python,慢,所以对于算法的要求也就高,求素数最弟弟的想法就是从2到n-1遍历每个数字求余判断,如下:
for i in range(2,n):
	if n%i==0:return False
return True

第一步优化,将上限改成n/2,因为一个数除以一个比它一半大的数肯定除不尽,因此再之后就可以,将上限改为√n,向下取整,因为如果n不是素数,那么存在n/p=q,pq大于1,如果p大于根号n,且n不是质数,则必定存在q小于根号n,(C/C++简化到这里就能AC了而且只要10几ms,然而用Python就会最后一个测试点超时QAQ)

for i in range(2,int(math.sqrt(n))):
    if n%i==0:return False
return True

第二步优化,众所周知偶数除了2都不是质数,所以可以只判断奇数是不是该数字的因子

if n%2==0:return False
for i in range(3,int(math.sqrt(n)),2):
	if n%i==0:return False
return True

因此也就适用于是否为3的倍数,5的倍数,7,11,13……衍生出下面的筛选法求素数,先建立素数表,再判断(判断过程略),这就是实现代码的原理

t=[1]*(n+2)
a=[]
p=2
while p<=n:
	a.append(p)
    for i in range(2*p,n+1,p):
        t[i]=0
    while True:
        p+=1
        if t[p]==1:break

后来突然发现,这个题是求孪生素数的题,验证孪生素数猜想在百度百科上发现有一个神奇的特性来判断这个数是不是素数,大于3的素数只分布在6n-16n+1两数列中,因此得到了最后的优化代码

if n%6!=1 and n%6!=5:return False #不在6倍数两侧的一定不是素数 
#然后判断6的倍数两侧的是不是素数
for i in range(5,int(math.sqrt(n)+1),6):
    if n%i==0 or n%(i+2)==0:return False
return True #排除所有,剩余的是质数

关于使用math.sqrt(n)函数和n**0.5, pow(n,0.5), 还有numpy库函数的区别,使用下面代码,利用time()函数分别计算运行时长,可以发现,正经算数学就用math的对应函数…具体其他不同还需要再仔细读函数,然后再测试,比如大数多次方这种计算…

from time import time
# import math
import numpy as np
n=10**8
t1=time()
a=[i**0.5 for i in range(n)]  #20.718587160110474
# a=[pow(i,0.5) for i in range(n)]  #25.581637859344482
# a=[math.sqrt(i) for i in range(n)]  #18.50152826309204
# a=[math.pow(i,0.5) for i in range(n)]  #28.30438256263733
# a=[np.sqrt(i) for i in range(n)]  #111.94471502304077
# a=[np.power(i,0.5) for i in range(n)]  #183.07159638404846
t2=time()
print(t2-t1)

PS: day7 别以为Python没有算法,当你的程序一样可以运行的很快的时候,你就领悟了算法的真谛 (调包万岁) 虽然还是慢…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值