数论——区间筛质数
引入
在质数讲解中我们提到可以使用线性筛,去筛选1~n之间的质数,时间复杂度是线性的。对于一段区间[l, r],我们可以依据这个思路,筛1~r的质数,然后通过二分法,找到左边界。然而这样做是有缺陷的。比如当我们r的范围很大时,这要的线性筛法会超时。最好的办法是,直接筛[l, r]之间的数。
下面具体讲一下实现思路。
思路
要想筛[l, r]之间的质数,借鉴筛质数的思路,我们需要知道[l, r]的质因数,通过埃氏筛法将[l, r]中所有相关的合数全部筛掉。一个合数x的最小质因子一定小于等于
x
\sqrt{x}
x,证明略。我们每次只需要用这个最小质因子,将所有相关的合数筛掉即可。
下面我们来看一道例题。
质数距离
给定两个整数 L 和 U,你需要在闭区间 [L,U] 内找到距离最接近的两个相邻质数 C1 和 C2(即 C2−C1
是最小的),如果存在相同距离的其他相邻质数对,则输出第一对。
同时,你还需要找到距离最远的两个相邻质数 D1 和 D2(即 D1−D2 是最大的),如果存在相同距离的其他相邻质数对,则输出第一对。
输入格式
每行输入两个整数 L
和 U,其中 L 和 U 的差值不会超过 10^6。
输出格式
对于每个 L 和 U,输出一个结果,结果占一行。
结果包括距离最近的相邻质数对和距离最远的相邻质数对。(具体格式参照样例)
如果 L 和 U 之间不存在质数对,则输出 There are no adjacent primes.。
数据范围
1≤L<U≤2^31−1
输入样例:
2 17
14 17
输出样例:
2,3 are closest, 7,11 are most distant.
There are no adjacent primes.
代码
sqrt(2 ** 31 -1)
46340.950001051984
from math import ceil
N = 1000010
st = [False] * N
primes = []
# 筛质数模板
def init(n) :
for i in range(2, n + 1) :
if not st[i] :
primes.append(i)
for j in primes :
if i * j > n :
break
st[i * j] = True
if i % j == 0 : break
try :
while True :
l, r = map(int, input().split())
primes = []
st = [False] * N
init(50010)
st = [False] * N
for i in primes : # 筛l~r之间的质数
j = max(ceil(l / i), 2) * i
while j <= r :
st[j - l] = True # 筛掉合数
j += i
primes = []
for i in range(r - l + 1) :
if not st[i] and i + l >= 2 :
primes.append(i + l)
length = len(primes)
if length < 2 :
print("There are no adjacent primes.")
continue
minn, maxx = 10000010, 0
minl, minr, maxl, maxr = 0, 0, 0, 0
for i in range(length - 1) :
if minn > primes[i + 1] - primes[i] :
minn = primes[i + 1] - primes[i]
minl, minr = primes[i], primes[i + 1]
if maxx < primes[i + 1] - primes[i] :
maxx = primes[i + 1] - primes[i]
maxl, maxr = primes[i], primes[i + 1]
print(f"{minl},{minr} are closest, {maxl},{maxr} are most distant.")
except : pass
总结
对于数论问题一定要发掘题目中的具体关系。