您的位置:首页 > 其它

poj2689(筛选法求素素)

2020-05-11 04:11 369 查看

ayit-2020-609第六周训练题
题目
The branch of mathematics called number theory is about properties of numbers. One of the areas that has captured the interest of number theoreticians for thousands of years is the question of primality. A prime number is a number that is has no proper factors (it is only evenly divisible by 1 and itself). The first prime numbers are 2,3,5,7 but they quickly become less frequent. One of the interesting questions is how dense they are in various ranges. Adjacent primes are two numbers that are both primes, but there are no other prime numbers between the adjacent primes. For example, 2,3 are the only adjacent primes that are also adjacent numbers.
Your program is given 2 numbers: L and U (1<=L< U<=2,147,483,647), and you are to find the two adjacent primes C1 and C2 (L<=C1< C2<=U) that are closest (i.e. C2-C1 is the minimum). If there are other pairs that are the same distance apart, use the first pair. You are also to find the two adjacent primes D1 and D2 (L<=D1< D2<=U) where D1 and D2 are as distant from each other as possible (again choosing the first pair if there is a tie).
Input
Each line of input will contain two positive integers, L and U, with L < U. The difference between L and U will not exceed 1,000,000.
Output
For each L and U, the output will either be the statement that there are no adjacent primes (because there are less than two primes between the two given numbers) or a line giving the two pairs of adjacent primes.
Sample Input
2 17
14 17
Sample Output
2,3 are closest, 7,11 are most distant.
There are no adjacent primes.
老实说对我这菜鸟来说,我不知道什么叫数论,也不知道什么筛选法。。
埃拉托斯特尼筛法
算式
给出要筛数值的范围n,找出以根号n内的素数P1,P2…Pk。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉;不断重复下去…。

步骤
详细列出算法如下:
1.
列出2以后的所有序列:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
2.
标出序列中的第一个素数,也就是2,序列变成:
*2 *3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
3.。
将剩下序列中,划摽2的倍数(用加粗标出),序列变成:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
4
如果现在这个序列中最大数小于最后一个标出的素数的平方,那么剩下的序列中所有的数都是素数,否则回到第二步。
比如求25以内素数:
因为25大于2的平方,我们返回第二步:
剩下的序列中第一个素数是3,将主序列中3的倍数划出(加粗),主序列变成:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
我们得到的素数有:2,3
25仍然大于3的平方,所以我们还要返回第二步:
现在序列中第一个素数是5,同样将序列中5的倍数划出,主序列成了:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
我们得到的素数有:2 3 5 。
因为25等于5的平方,跳出循环.
结论:去掉红色的数字,2到25之间的素数是:2 3 5 7 11 13 17 19 23
既然已经了解了 我们来小试一下

比如 求n以内得素数用筛选法

#include <stdio.h>
#include <math.h>
using namespace std;
int prime[10000000];///存放需要筛选数
int sieve (int *prime,int n,int k)///n是1-n这个范围,k是当前把prime[k]作为筛子
{
for (int i= k+1; i<=n ; i++)
{
///当前数prime[i]不为0且能整出筛子数,说明肯定是合数
if (prime[i] && prime[i]%prime[k]== 0)
{
prime[i] = 0;///将合数赋值为0
}
}
k++;
///如果当前筛子数是0,则移到下一位
while (!prime[k])
{
k++;
}
return k;
}
int main()
{
int  n;
while (scanf("%d",&n)&& n)
{
int k = 2;///从素数2开始
for (int i = 1; i <= n; i++)
{
prime[i] = i;
}
while (prime[k]<= sqrt(n))
{
k = sieve(prime,n,k);///找到下一个筛子
}
for (int i = 2; i<= n; i++)
{
if (prime[i]!= 0)
{
printf("%d ",prime[i]);
}
}
}
return 0;
}

简直很神奇。好了根据已有知识开始做题
对于本题 我们只需找出【L,U】内的素数记录距离
根据上述4可利用根号N以内的素数作为筛子求出区间内的素数

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e5;
int prime[maxn];
int tot;
void get_pri(int n)
{
bool v[n+5];
memset(v,0,sizeof(v));
for(int i=2; i<=n; i++)
{
if(!v[i])
prime[++tot] = i;///存为根号N内筛选数
for(int j=i; j<=n/i; j++)
{
v[j*i] = 1;
}
}
}
int main()
{
int l,r;
while(~scanf("%d%d",&l,&r))
{
tot = 0;
get_pri(sqrt(r));
bool vis[r-l+6];///记录所需区间内的数
memset(vis,0,sizeof(vis));
for(int i=1; i<=tot; i++)///遍历筛子
{
for(int j=ceil(l*1.0/prime[i])/*向上取整,因为是闭区间*/; j<=r/prime[i]; j++)///j表示的是筛子要到达本区间需要乘的倍数
{
if(j == 1)///如果为1倍则已经是区间内的素数
continue;
vis[prime[i]*j-l] = 1;///将筛子的倍数标记
}
}
int ans[r-l+5];///记录区间内的所有素数
int cnt = 0;
for(int i=0; i<=r-l; i++)///遍历区间 将l看为0
{
if(!vis[i])///如果不是筛子的倍数
{
if(i+l == 1)///排除1
continue;
ans[++cnt] = i+l;///记录位置 从0开始需加1
}
}
if(cnt < 2)
printf("There are no adjacent primes.\n");
else
{
int minn = 0x3f3f3f3f;
int maxx = 0;
int id1;
int id2;
for(int i=1; i<cnt; i++)
{
int tmp = ans[i+1]-ans[i];
if(tmp < minn)
{
minn = tmp;
id1 = i;
}
if(tmp > maxx)
{
maxx = tmp;
id2 = i;
}
}
printf("%d,%d are closest, %d,%d are most distant.\n",ans[id1],ans[id1+1],ans[id2],ans[id2+1]);
}
}
}

我又搬运了学长的,比较理解 并带有注释

在这里插入代码片
#include<stdio.h>
#include<string.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MN=1000010;
int g,p[MN];///存放根号n之前的素数
bool vis[MN],bk[MN];
void prime()
{
for(int i=2; i<MN; i++)///倍数
{
if(!vis[i])
p[++g]=i;
for(int j=1; j<=g && i*p[j]<MN; j++)
{
vis[i*p[j]]=1;
if(i%p[j]==0)///p[j]存放筛数
break;
}
}
}
int main()
{
prime();
ll L,R;
while(~scanf("%lld %lld",&L,&R))
{
memset(bk,0,sizeof(bk));
if(L==1)///排除1
bk[0]=1;
for(int i=1; p[i]*p[i]<=R && i<=g; i++)///遍历筛子--即遍历根号R之前的素数
{
ll he;///这里he 记录的是某一筛子进入区间的边界
if(L%p[i]) ///如果L不是某一素数的倍数
he=(L/p[i]+1)*p[i];///倍数(向上取整)乘以素数 边界从L之后且是p[i]的倍数开始
else he=L;///如果L是p[i]的倍数,那边界就从L开始
if(he==p[i])///如果边界本身是筛子
he+=p[i];///就从他下一个倍数开始变为边界
for(ll j=he; j<=R; j+=p[i])///从左边界到右边界
bk[j-L]=1;///将筛子的倍数标记
}
///接下来就是确定最小临素数 和最大临素数
ll last=-1,mi=R,ma=0,mil,mir,mal,mar;
for(ll i=L; i<=R; i++)
{
if(!bk[i-L])
{
if(last!=-1 && ma<i-last)
{
ma=i-last;
mal=last;
mar=i;
}
if(last!=-1 && mi>i-last)
{
mi=i-last;
mil=last;
mir=i;
}
last=i;
}
}
if(mi==R)
printf("There are no adjacent primes.\n");
else
printf("%lld,%lld are closest, %lld,%lld are most distant.\n",mil,mir,mal,mar);
}
return 0;
}
Starry_Sky_Dream 原创文章 50获赞 4访问量 2358 关注 私信
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: