素数_埃氏筛法&&线性筛(欧拉筛法)【详解】

埃氏筛法:

整数的唯一分解定理:

任何一个大于1的自然数 N,如果N不为质数,都可以唯一分解成有限个质数的乘积N=P1 ^ a1 · P2 ^ a2 · P3 ^ a3 ·· Pn ^ an ,这里P1 (:当然质数的话直接就是质数本身)

埃氏筛法的思想:对于不超过N的素数p,删除2p,3p,4p…,当处理完所有的数之后,还没有被删除的数就都是素数。

O(nloglogn)

bool vis[maxN];

void init()
{
    memset(vis,false, sizeof(vis));//所有数初始化为0->质数
    vis[0]=true;
    vis[1]=true;
}

void Is_Prime()
{
    init();
    for(int i=2;i<=N;i++)
    {
        if(!vis[i])
            for(int j=i*i;j<=N;j+=i)
                vis[j]=true;
    }
}

对于内层循环的 j=i*i 的解释:
因为2i, 3i, 4i, … , (i-1)i 肯定已经被前面的2, 3, 4, … , (i-1) 这些里面的质数筛掉了,所以说从 j=i*i 开始可以优化这里。
换句话说,我们筛掉的是素数的倍数,那么i的倍数中包括2, 3, … i, …, m,但是比i小的那些倍数,2, 3, … i-1,在i作为素数开始筛合数之前肯定已经i被当作2, 3, … i-1这些中素数的倍数被筛掉了,所以我们只需要从j=i*i 开始即可。

  • 但是,埃氏筛法无法避免重复地筛一些数,比如说30=2 * 15=3 * 10=5 * 6,所以为了避免重复筛数,我们有线性筛

线性筛(欧拉筛法)

  • 线性筛是在埃氏筛法的基础上,让每一个合数,只被它的最小质因子筛选一次,达到不重复的目的。
  • 怎么理解呢?根据最开始所说的整数的唯一分解定理,每个合数有且只有一组质数相乘可以得到,前面的埃氏筛法,每一个质因子都要筛掉该合数一次,我们如果让每个合数只被它最小的质因子筛选就完全避免了埃氏筛法的重复。从算法来说,N范围内的每个数都只会被筛一次,所以算法复杂度是O(n)。

O(n)

bool vis[maxN];
int prime[maxN],cnt;

void init()
{
    memset(vis,false, sizeof(vis));//所有数初始化为0->质数
    vis[0]=true;
    vis[1]=true;
    cnt=0;
}

void Is_Prime()
{
    init();
    for(int i=2;i<=N;i++)
    {
        if(!vis[i])//i是质数
            prime[++cnt]=i;//prime是用来存质数的数组,显然数组中的质数是从小到大
        for(int j=1;j<=cnt;j++)
        {
            if(i*prime[j]>N)//超出了范围
                break;
            vis[i*prime[j]]=true;
            if(i % prime[j] == 0)//跑到了i的最小的质因数
                break;
        }
    }
}

代码解释:
我们知道一个整数n的最大质因子不超过n^(1/2),又因为我们的prime数组内的质数是单调递增的,所以我们可以保证对于外层for循环跑到 i 的时候,它的最大质因子已经存在于prime[ ]中了。所以我们也一定能跑到它最小的质因子。
关于vis[i*prime[j]]=true; 的解释,i * prime[ j ]也就是质数prime[ j ]作为最小质因子能够筛掉的部分合数。这里我们跑的是j的循环,也就是把 prime[ ] 中记录的素数,升序来当作要消去合数的最小质因子,所以是对于整数 i (无论 i 是合数还是质数), i * prime[ j ]都是合数会被筛掉。
举个例子:i=6,6的最小质因子是2,这时候我们可以筛掉 6 * 2=12;然后就结束了。如果不结束,接着下一个质数是3,那么我们筛掉的就是18=6 * 3=2 * 3 * 3,(这里显然3不是18的最小质因子,违背了我们的算法)。18的最小质因子是2,那么18就会被重复筛(当i=9时,9的最小质因子是3,会筛掉9 * 2=18,9 * 3=27)。至于 i 本身就是质数,那么它的最小质因子就是它本身。
所以这就是为什么if(i % prime[j] == 0) 会存在了,我们必须保证只跑到i的最小质因子就结束。因为我们必须保证prime[ j ] 是i * prime[ j ]的最小质因子才行!

这是线性筛的模板题:
洛谷 P3383 【模板】线性筛素数

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxN=1e7+5;
const int maxC=1e6+5;

int N,M;
bool vis[maxN];
int prime[maxN],cnt;

void init()
{
    memset(vis,false, sizeof(vis));//所有数初始化为0->质数
    vis[0]=true;
    vis[1]=true;
    cnt=0;
}

void Is_Prime()
{
    init();
    for(int i=2;i<=N;i++)
    {
        if(!vis[i])//i是质数
            prime[++cnt]=i;
        for(int j=1;j<=cnt;j++)
        {
            if(i*prime[j]>N)//超出了范围
                break;
            vis[i*prime[j]]=true;
            if(i % prime[j] == 0)//跑到了i的最小的质因数
                break;
        }
    }
}

int main()
{
    while(~scanf("%d%d",&N,&M))
    {
        Is_Prime();
        while(M--)
        {
            int num;
            scanf("%d",&num);
            if(!vis[num])
                printf("Yes\n");
            else
                printf("No\n");
        }
    }
    return 0;
}

你可能感兴趣的:(数论)