筛法

1.筛法简介

       筛选法又称筛法,具体做法是:先把N个自然数按次序排列起来。1不是质数,也不是合数,要划去。第二个数2是质数留下来,而把2后面所有能被2整除的数都划去。2后面第一个没划去的数是3,把3留下,再把3后面所有能被3整除的数都划去。3后面第一个没划去的数是5,把5留下,再把5后面所有能被5整除的数都划去。这样一直做下去,就会把不超过N的全部合数都筛掉,留下的就是不超过N的全部质数。

//首先先说一下最朴素的判断素数的方法

int prime(int n)
{
	for(int i=2;i<sqrt(n);i++)
	{
		if(i%n==0)
		return 0;//不是素数 
	}
	return 1;//是素数 
 } 

可以看出最多需要遍历 \sqrt{n}次才能判断出来n是素数时间复杂度相对来说比较高,但是我们看接下来这种算法,时间会减少许多

//分为两大类//

· 埃氏筛法(埃拉托斯特尼筛法)

基本思想:

要得到自然数n以内的全部素数,必须把不大于\sqrt{n}的所有素数的倍数剔除,剩下的就是素数。  

给出要筛数值的范围n,找出以内的素数。先用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 5 7 9 11 13 15 17 19 21 23 25
  4. 如果这个序列中最大数小于最后一个标出的素数的平方,那么剩下的序列中所有的数都是素数,否则回到第二步。

  5. 本例中,因为25大于2的平方,我们返回第二步:

  6. 剩下的序列中第一个素数是3,将主序列中3的倍数划掉,主序列变成:

    2 3 5 7 11 13 17 19 23 25
  7. 我们得到的素数有:2,3

  8. 25仍然大于3的平方,所以我们还要返回第二步:

  9. 序列中第一个素数是5,同样将序列中5的倍数划掉,主序列成了:

    2 3 5 7 11 13 17 19 23
  10. 我们得到的素数有:2,3,5 。

  11. 因为23小于5的平方,跳出循环.

结论:2到25之间的素数是:2 3 5 7 11 13 17 19 23。

1.判断一个数n是否为素数

首先全部都定义为true,假设全部都是素数,然后从2开始判断是不是素数,是的话进行下一次循环。首先把2*2,然后这个2*2的结果一定不是素数,就把它改成false,然后让这个结果加上2,加上2之后也不是素数,也改成false,直到小于等于规定的最大数的时候停止,继续外层的循环。

一个数乘2之后的结果一定不是素数,因为他有个因子2了,然后这个数再乘之后再不断加上它本身的结果也不是素数,因为这个因子2在不断增大

bool s[maxn];//第i个素数 
int prime[maxn];//结果为true表示i为素数 
void sieve(int n)
{
	int cnt=0;
	for(int i=0;i<=n;i++)
	prime[i]=true;//先全部置为真 
	prime[0]=prome[1]=false;//0,1不是素数 
	for(int i=2;i<=n;i++)//从2开始往后筛 
	{
		if(prime[i])
		s[cnt++]=i;//i是下一个素数 
		for(int j=2*i;j<=n;j+=i)
		s[j]=false;
	}
}

为了避免重复筛,可以使用另一种算法欧拉筛 :

原则:每一个合数只会被它的最大非自身因数筛选(对应最小质因数),就是保证每个合数只会被筛选一次。

2 加入 p 数组,枚举质数(只有2),筛去质数与 2 的乘积(筛去 4)。3 加入 p 数组(现在有2、3),枚举质数,筛去 3 与质数的乘积(筛去6、9)。4 不是质数,不加入 p 数组,枚举质数,筛去8;此时 4mod2=0 , break。

i质数筛去的合数
224
32,36,9
42,38
52,3,510,15,25
.........

bool a[maxn];
int prime[maxn],cnt;
void sieve(int n)
{
for(int i=2;i<=n;i++)
{
	if(a[i]==false)
		p[cnt++]=i;//是质数就记录;
	for(int j=1;j<=cnt && i*prime[j]<=n;j++)
	{
		a[i*prime[j]]=true;//标记合数;
		if( i%p[j] == 0 )
			break;//保证同一个合数不会被多个质数标记。
	}
}

例题:

1.蒜头君对既是素数又是回文的数特别感兴趣。比如说 151151 既是素数又是个回文数。回文数是指从左到右读和从右到左读都一样的数。

现在小王想要你帮助他找出某个范围内的素数回文数,请你写个程序找出 aa 跟 bb 之间(包含 aa 和 bb)满足条件的数。

输入格式

输入 aa 和 b(2 \le a < b \le 100,000,000)b(2≤a<b≤100,000,000)。

输出格式

按从小到大输出 a, ba,b 之间所有满足条件的素数回文数,一个数占一行。

Sample 1

InputcopyOutputcopy
2 200
2
3
5
7
11
101
131
151
181
191

 代码如下:

#include<stdio.h>
#define N 10411411
int c[N];
void prime()    //筛选素数
{
	 int i,j;
	 for(i=0; i<N; i++)
	  c[i]=1;
	 c[0]=c[1]=0;
	 for(i=2; i<N; i++)
	 {
	  if(c[i]==1)
	  {
	   for(j=2*i; j<N; j+=i)
	    c[j]=0;
	  }
	 }
}
	
int huiwen(int n)
{
	int x=0,s=n;
	while(s)
	{
		x=x*10+s%10;
		s=s/10;
	}
	if(x==n)
	return 1;
	return 0;
}
	
int main()
{
	 int a,b;
	 scanf("%d%d",&a,&b);
	 prime();
	 if(b>10411401)
	 b=10411401;
	 for(int i=a; i<=b; i++)
	 {
	  if(c[i]==1 &&huiwen(i))
	   printf("%d\n",i);
	 }
    return 0;
}

 

2.蒜头君定义两个相差为 22 的素数称为素数对,如 55 和 77,1717 和 1919 等,要求找出所有两个数均不大于 nn 的素数对。

输入格式

一个正整数 n。100001≤n≤10000。

输出格式

所有小于等于 n 的素数对。每对素数对输出一行,中间用单个空格隔开。若没有找到任何素数对,输出"empty"

Sample 1

InputcopyOutputcopy
100
3 5
5 7
11 13
17 19
29 31
41 43
59 61
71 73

 代码如下:

#include<bits/stdc++.h>
using namespace std;

int p(int n)
{
	if(n==2)
	return 1;
	for(int i=2;i<=sqrt(n);i++)
	if(n%i==0)
	return 0;
	return 1;
}
int main(void)
{
	int n;
	cin>>n;
	int flag=0;//是否有素数对 
	for(int i=2;i<=n-2;i++)//防止另一个数超过i+2过n的范围 
	{
		if(p(i)&&p(i+2))
		cout<<i<<" "<<i+2<<endl;
		flag=1;//有素数对 
	}
	if(flag==0)
	cout<<"empty"<<endl; 
	return 0;
}

   

3.Given a number n, please count how many tuple(p1, p2, p3) satisfied that p1<=p2<=p3, p1,p2,p3 are primes and p1 + p2 + p3 = n.

Input

Multiple test cases(less than 100), for each test case, the only line indicates the positive integer n (n \leq 10000)n(n≤10000).

Output

For each test case, print the number of ways.

Sample

InputcopyOutputcopy
 
3 9
 
0 2
#include<bits/stdc++.h>
using namespace std;
int p[10010];
void sieve()
{
    memset(p,1,sizeof(p));
    p[0]=p[1]=0;
    for(int i = 2; i <= 10000; i++)
    {
        if(p[i] == 1)
        {
            for(int j = 2*i; j <= 10000; j+=i)
            {
                p[j] = 0;
            }
        }
    }
}
int main()
{
    int a[10017];
    int n;
    sieve();
    while(~scanf("%d",&n))
    {
        int cont = 0;
        for(int i = 2; i <= n; i++)
        {
            if(p[i] == 0)
                for(int j = i; j <= n; j++)
                {
                    if(p[j] == 0 && p[n-i-j] == 0)
                        if(n-i-j>=i && n-i-j>=j)
                        {
                            cont++;
                        }
                }
        }
        printf("%d\n",cont);
    }
    return 0;
}

4.Everybody knows any number can be combined by the prime number.
Now, your task is telling me what position of the largest prime factor.
The position of prime 2 is 1, prime 3 is 2, and prime 5 is 3, etc.
Specially, LPF(1) = 0.

Input

Each line will contain one integer n(0 < n < 1000000).

Output

Output the LPF(n).

Sample

InputcopyOutputcopy
 
1 2 3 4 5 
 
0 1 2 1 3 

代码如下:

先把所有值都初始化为0,if的第一次判断就直接成立,position 就变成了1。然后数组2的位置,以及后面2的倍数的位置就都是1了,也代表着这些数都不是素数。接下来就是3,数组3的位置也是0,if成立,所以position再加1变成2,然后3以及3的倍数就都变成2。遇到素数了position加一,也相当于是记录素数的个数的一个变量,然后在数组中让这些是素数以及素数的倍数的数,标记为这个position。一个被标记完后可以再次被标记,因为第一次不一定是最大的素数因子,比如10,第一次是因子2,下次再被标记的时候就是因为因子5被标记。

#include<bits/stdc++.h>
using namespace std;
int a[1000010];

int main(void)
{
	memset(a,0,sizeof(a));
	a[1]=0;
	int k=0;
	for(int i=2;i<1000000;i++)//不是sqrt 
	{
		if(!a[i])
		{
			k++;
			for(int j=i;j<1000000;j+=i)
			a[j]=k;
		}
	}
	int n;
	while(scanf("%d",&n)!=EOF)
	printf("%d\n",a[n]);
	return 0;
 } 

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易哈哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值