质数打表———埃氏筛&&欧拉筛

质数:一个大于1的自然数,除了1和他本身没有其他因数的数叫做质数(素数),否则称为合数。

如何判断一个数是否为质数:

  1. 从2枚举到n-1,判断是否存在n的因子,复杂度O(n)
  2. 从2枚举到   n \ \sqrt[]{n}  n ,复杂度O(n),因为n的因子总是成对出现的,若m是它的因子,那么n/m也是它的因子,所以不用再遍历   n \ \sqrt[]{n}  n ~ n n n的数。

质数的筛法:

1. 逐个判断,O( n   n n\ \sqrt[]{n} n n )

2. 利用质数的性质,O(   n   n 3 \ {n\ \sqrt[]{n}\over 3}  3n n )

性质:每个(大于等于5的)质数都可以表示为6 x x x ± \pm ± 1,所以遍历是我们只用判断6 x x x+1和6 x x x-1是否为质数

3. 埃氏筛法,O( n l o g l o g n nloglogn nloglogn)

  • 基本思想 :唯一分解定理,比如:168=2 * 2 * 2 * 3 * 7=23 * 3 * 7,所以每个合数都可以表示成多个质数的乘积,也可以说它是某个质数的倍数。
  • 算法实现 :从2开始,将每个质数的倍数都标记成合数,以达到筛选素数的目的。

代码:


bool vis[manx];
void make_prime(int n)
{
     memset(vis,0,sizeof(vis));//0表示素数,1表示合数
     vis[0]=vis[1]=1;
     for(int i=2;i<=n;i++)
     {
          if(!vis[i])
          {
               for(int j=i+i;j<=n;j+=i)
               /*这里可以优化成for(int j=i*i;j<=n;j+=i),但注意i*i会不会爆int*/
                    vis[j]=1;
          }
     }
}

4. 线性筛(欧拉筛法),O( n n n)

  • 基本思想:上面埃氏筛法说道,从2开始,将每个质数的倍数都标记成合数,这样就会出现一个问题——同一个数会被重复筛选,比如168被2筛选了之后,还会被3、7再筛选一次,现在我们让每个合数只被它的最小公约数筛选一次,这就是欧拉筛选。

代码:

int prime[manx];
bool vis[manx];
void make_prime(int n)
{
     int cou=0;//素数的个数
     memset(prime,0,sizeof(prime));
     memset(vis,0,sizeof(vis));
     vis[0]=vis[1]=1;//0和1不是素数
     for(int i=2;i<=n;i++)
     {
          if(!vis[i])
               prime[cou++]=i;//记录素数
          for(int j=0;j<cou;j++)
          {
               if(i*prime[j]>n)break;
               vis[i*prime[j]]=1;
               if(i%prime[j]==0)break;
          }
     }
}
  • 解释:虽然欧拉筛和第三种埃氏筛思路相近,都是用质数去筛合数,都有两层循环,但它们代码实现的思路却不一样。埃氏筛是直接遍历数组vis(即1~n的数),遇到质数就把这个质数的倍数(1 ~n范围内)全都标记一遍。

  • 欧拉筛是把第一层循环的i作为质数的倍数,一步一步地筛去数组prime中存的质数的1倍、2倍、、、n倍,优化的关键点就在于if(i%prime[j]==0)break;因为prime是一个有序的数组,存放了从小到大的质数,当i%prime[j]==0时,i=prime[j] *k(其中k≥prime[j],因为prime[j]是i的最小因子),那么i *prime[j+x]就可以表示成prime[j]*prime[j+x]*k,所以i *prime[j+x]会在以后i=prime[j+x]*k时,被prime[j]筛掉。(也就是说,当i%prime[j]==0时,因为i *prime[j+x]有更小的因子prime[j]所以它不应该在此时被素数prime[j+x]筛去)

  • 借用博客中输出的打表的方法
    在这里插入图片描述
    可以看到运行到i=6时,并没有将6 * 3和6 * 5筛掉,而18和30这两个数分别在i=9和i=15时被2筛掉了

例题:P3383 【模板】线性筛素数

#include <iostream>
#include <iostream>
#include <string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int manx=1e7+10;
const int INF=1e9;
int n,m;
int prime[manx];
bool vis[manx];
void make_prime(int n)
{
     int cou=0;
     memset(prime,0,sizeof(prime));
     memset(vis,0,sizeof(vis));
     vis[0]=vis[1]=1;
     for(int i=2;i<=n;i++)
     {
          if(!vis[i])
               prime[cou++]=i;
          for(int j=0;j<cou;j++)
          {
               if(i*prime[j]>n)break;
               vis[i*prime[j]]=1;
               if(i%prime[j]==0)break;
          }
     }
}
int main()
{
    int num;
    make_prime(manx);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
         scanf("%d",&num);
         /*if(num==1)
         {
              printf("Yes\n");
              continue;
         }*/
         if(vis[num])
          printf("No\n");
         else
          printf("Yes\n");
    }
}

用埃筛和欧拉筛进行欧拉函数打表:求欧拉函数值 && 打表O(n)

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值