素数/质数判定 从判定素数/质数到埃氏筛和欧拉筛

假期集训第二轮是数论,是一直以来不愿意动手的一个分支,忽然发现什么都不会了QAQ。今天先写素数筛。
参考洛谷博客 zybnxy大佬 %%%一下

1. 判断一个数是素数 时间复杂度为O(N)

bool Is_prime(int n);
{
    if(n==1)return false;
    if(n==2)return true;
    for(int i=2;i<n;i++)
        if(n%i==0)return false;
    return true;    
}

很简单的板子,但是应用在数据较大较多的题目,一定是不可能的。

优化

bool Is_prime(int n);
{
    if(n==1)return false;
    if(n==2)return true;
    for(int i=2;i<=sqrt(n);i++)
        if(n%i==0)return false;
    return true;    
}

在这里插入图片描述
然后是6N±1的优化,时间复杂对可以达到
在这里插入图片描述
具体讲解是huang_miao_xin大佬

bool Is_prime(int n)
{
    if(n==1) return false;
    if(n==2||n==3) return true;
    if(n%6!=1&&n%6!=5) return false;//不在6的倍数两侧的一定不是质数
    for(int i=5;i*i<=n;i+=6)
        if(n%i==0||n%(i+2)==0) return false;
    return true;
}

测试到40W,时间大概是这样的:
素数/质数判定 从判定素数/质数到埃氏筛和欧拉筛_第1张图片

2. 素数筛

  1. 一般筛法(埃拉托斯特尼筛法):
    素数的倍数一定不是素数
    先初始化一个全是true的数组,代表全是素数,从第一个素数2开始,把2的倍数都标记为非素数(置为false),一直到大于N;然后进行下一趟,找到2后面的下一个素数3,进行同样的处理,直到最后,数组中依然为true的数即为素数。
    下图1应该也是黄色,因为1不算是素数,初始化为0素数/质数判定 从判定素数/质数到埃氏筛和欧拉筛_第2张图片
    素数/质数判定 从判定素数/质数到埃氏筛和欧拉筛_第3张图片

#include 
using namespace std;
const int MAXN=1000010;
bool prime[MAXN];
void make_prime)()
{
    memset(prime,true,sizeof(prime));//这里注意,memset只能用来置0和-1,
    						//emmm虽然一般它能给你换,但是,,,保险点用手动
    prime[0]=prime[1]=false;
    int t=sqrt(MAXN);
    for(int i=2;i<=t;i++)
    {
        if(prime[i])
        {
            for(int j=2*i;j<MAXN,j+=i)
			    //优化:如果j 从 i * i 而不是从 i + i开始,
				//因为 i*(2~ i-1)在 2~i-1时都已经被筛去,所以从i * i开始。
            {
                prime[j]=false;
            }
        }
    }
    return;
}

通过上述代码,我们发现此方法还可以继续优化,因为上述的方法中,每一个有多组因数可能会被筛多次,例如:30会被2,3,5各筛一次导致计算冗余,我们可以对此方法进行改进,得到更加高效的筛法 -----------------欧拉筛线性筛

欧拉筛法的基本思想 :在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。

#include 
using namespace std;
const int MAXN=1000010;
bool prime[MAXN];
int Prime[MAXN];
int num=0;
void make_prime()
{
    memset(prime,true,sizeof(prime));
    prime[0]=prime[1]=false;
    for(int i=2;i<=MAXN;i++)
    {
        if(prime[i])
        {
            Prime[num++]=i;
        }
        for(int j=0;j<num&&i*Prime[j]<MAXN;j++)
        {
            prime[i*Prime[j]]=false;
            if(!(i%Prime[j]))//解释见下
                break;
        }
    }
    return;
}

当 i是prime[j]的倍数时,i = kprime[j],如果继续运算 j+1,i * prime[j+1] = prime[j] * k prime[j+1],这里prime[j]是最小的素因子,当i = k * prime[j+1]时会重复,所以才跳出循环。
eg:i = 8 ,j = 1,prime[j] = 2,如果不跳出循环,prime[j+1] = 3,8 * 3 = 2 * 4 * 3 = 2 * 12,在i = 12时会计算。因为欧拉筛法的原理便是通过最小素因子来消除。

程序理解见下图,图源自彤云望月大佬
素数/质数判定 从判定素数/质数到埃氏筛和欧拉筛_第4张图片
这个方法可以保证每个合数在N/M的最小素因子时被筛出,所以时间复杂度仅为O(N)已经可以满足大部分需求,在竞赛中,这种方法也可以在极短的时间内求出关于N的质数表。

ps:每个正整数都能够以唯一的方式表示成它的质因数的乘积。详情见百度百科。
eg:素数/质数判定 从判定素数/质数到埃氏筛和欧拉筛_第5张图片

但是这个算法的弊端在于,为了判断一个大数是否是素数必须从从头开始扫描,而且空间代价也受不了,故不能离散的判断。

所以,我们要引入更高效的算法——

Miller_Rabin算法

miller_rabin是一种素性测试算法,用来判断一个大数是否是一个质数。 miller_rabin是一种随机算法,它有一定概率出错,设测试次数为s,那么出错的概率是 在这里插入图片描述

emmm想看的去 zybnxy 大佬题解里看吧
我,,,没搞懂,而且不稳定,不敢用。。。
素数/质数判定 从判定素数/质数到埃氏筛和欧拉筛_第6张图片

你可能感兴趣的:(数论,ACM集训,素数筛)