功能
最快、最合适地判断一个数为素数
说明
分为打表法和单个判断法两类方法
打表法 是开始时将所有素数标记出来,适合多次调用判断,前两种属于打表法
单个判断法 则是只一个数一个数判断,适合少量判断来节省时间,后俩种属于单个判断法
四种方法:
方法一:素数筛(埃氏筛)(n<10^7)
N只需等于MAX的一半(因为j从i*i开始),这个算最常用的
#define MAX 10000005
#define N 4000
int vis[MAX]={1,1};
void Prime(){
for(int i=2;i<= N;i++){
if(!vis[i]){
for(int j=i*i;j<=MAX;j+=i)
vis[j]=1; //0为素数
}
}
}
方法二:欧拉筛 (n<10^7)
据说时间上接近O(n),实际上没有试过,空间复杂度基本一样
但欧拉筛的过程可以得到其最小的素数因子,有的题要使用改进后的欧拉筛
const long long MAX=10000005;
int prime[MAX];
int vis[MAX]={1,1};
void Prime(){
for (int i = 2;i <= MAX; i++) {
if (!vis[i])
prime[++prime[0]] = i;
for (int j = 1; j <=prime[0] && i*prime[j] <=MAX; j++) {
vis[i*prime[j]] = 1; //1为素数
if (i % prime[j] == 0)
break;
}
}
}
方法三:遍历法 (n<10^9)
最经典的方法,优化后,只需判断从2到即可
虽然复杂度较大,但对于单个判断还是很适用的。范围判断也可以判断1-1e6的所有次数,一般是用的最多的
bool fin(int x)
{
if(x==1) return false;
for(int i=2;i*i<=x;i++)
if(x%i==0) return false;
return true; //1为素数
}
方法四:Miller-Rabin算法 (n<10^18)
大概率地判断是否为素数 详情见博客:Miller-Rabin算法
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
int prime[10]={2,3,5,7,11,13,17,19,23,29};
ll mul(ll a,ll b,ll c) //快速积
{
ll ans=0,res=a;
while(b)
{
if(b&1)
ans=(ans+res)%c;
res=(res+res)%c;
b>>=1;
}
return (ll)ans;
}
ll qpow(ll a,ll b,ll c) //快速幂
{
ll ans=1,res=a;
while(b)
{
if(b&1)
ans=mul(ans,res,c);
res=mul(res,res,c);
b>>=1;
}
return ans;
}
ll pri(ll x) //判断素数,1为素数
{
ll i,j,k;
ll s=0,t=x-1;
if(x==2) return 1;
if(x<2||!(x&1)) return 0;
while(!(t&1))
{
s++;
t>>=1;
}
for(i=0;i<10&&prime[i]<x;++i)
{
ll a=prime[i];
ll b=qpow(a,t,x);
for(j=1;j<=s;++j)
{
k=mul(b,b,x);
if(k==1&&b!=1&&b!=x-1)
return 0;
b=k;
}
if(b!=1) return 0;
}
return 1;
}