质数判定方法
update on 2018/12/01 12:51
最前边的一些话
-
如果你连素数都不知道是什么,我建议你可以试试滚学科了
-
如果你可以秒切这道题,那么这篇文章也帮不了你这个dalao,你也可以
滚去看别的文章了 -
这篇文章的源码基本有10k,LZ在后边敲的时候都一卡一卡的,希望能耐心看下去,
另外来骗个赞
0:一些前言
在
接下来,我们将要学习这些方法。
另:lz不太会用\LaTeX ,凑合看吧
1:如何判断一个数是不是质数。
- 1:根据定义:素数就是一个数除了
1 和他本身没有其他因数的数叫做质数。
于是我们对于一个
代码如下:
bool Is_prime(int n);
{
if(n==1)return false;
if(n==2)return true;
for(register 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(register int i=2;i*i<=n;i++)
if(n%i==0)return false;
return true;
}
上述程序时间复杂度为
优化2
根据lz多年的经验,已经达到
证明:令
可以看到,不在6的倍数两侧,即
所以它们一定不是素数,再除去
所以,基于以上条件,我们假如要判定的数为
但是
但是
即循环的步长可以定为
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;
for(register int i=5;i*i<=n;i+=6)
if(n%i==0||n%(i+2)==0) return false;
return true;
}
理论上讲整体速度应该会是优化1代码的三倍的3倍。即
暂且就这么认为吧
小结:在对于素数的判断时,一般用第一种的优化方法就可以了,但是如果遇到了毒瘤出题人,最好是用第二种方法。
2:质数的筛法
0:使用第一种优化的方法从
代码:
bool Is_prime(int n);
{
if(n==1)return false;
if(n==2)return true;
for(register int i=2;i*i<=n;i++)
if(n%i==0)return false;
return true;
}
const int N=100010
bool prime[N];
void make_prime()
{
for(ri i=1;i<=N;i++)
prime[i]=Is_prime(i);
return;
}
1:一般筛法(埃拉托斯特尼筛法):
基本思想:素数的倍数一定不是素数
实现方法:用一个长度为
例如,我们要求
我们先把
然后我们找到下一个素数
然后我们又找到了
我们重复着上述过程,完成了对
另附这里有一张动图,还不懂的同学可以参照这幅图理解
代码如下:
#include<cstring>
#include<cmath>
const int MAXN=1000010;
bool prime[MAXN];
void make_prime)()
{
memset(prime,true,sizeof(prime));
prime[0]=prime[1]=false;
int t=sqrt(MAXN);
for(register int i=2;i<=t;i++)
{
if(prime[i])
{
for(register int j=2*i;j<MAXN,j+=i)
{
prime[j]=false;
}
}
}
return;
}
此方法的复杂度为
通过上述代码,我们发现此方法还可以继续优化,因为上述的方法中,每一个有多组因数可能会被筛多次,例如:
代码如下:
#include<cstring>
#incldue<cmath>
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;
}
这个方法可以保证每个和合数在
但是这个算法的弊端在于,为了判断一个大数是否是素数必须从从头开始扫描,而且空间代价也受不了,故不能离散的判断。
所以,我们要引入更高效的算法——Miller_Rabin算法。
Miller_rabin算法描述
首先介绍一下
miller_rabin是一种素性测试算法,用来判断一个大数是否是一个质数。 miller_rabin是一种随机算法,它有一定概率出错,设测试次数为
下面开始介绍miller_rabin的做法。
首先我们知道,根据费马小定理,如果
如果我们让
的
若
那么
因为
所以
因为
所以
所以只能是
由此我们可以发现
那么,我们可以证明出
我们可以根据这个性质进行二次探测。
如果我们设要测试的数为
至于为什么在代码中这么做是对的,我看到一个很好的解释,在这里分享一下。
那么我们把
代码实现如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#define ll long long
using namespace std;
const int times = 20;
int number = 0;
map<ll, int>m;
ll Random(ll n)//生成[ 0 , n ]的随机数
{
return ((double)rand()/RAND_MAX*n+0.5);
}
ll q_mul(ll a, ll b, ll mod)//快速计算 (a*b) % mod
{
ll ans=0;
while(b)
{
if(b&1)
{
b--;
ans=(ans+a)%mod;
}
b/=2;
a=(a+a)%mod;
}
return ans;
}
ll q_pow(ll a,ll b,ll mod)//快速计算 (a^b) % mod
{
ll ans=1;
while(b)
{
if(b&1)
{
ans=q_mul(ans,a,mod );
}
b/=2;
a=q_mul(a,a,mod);
}
return ans;
}
bool witness(ll a,ll n)//miller_rabin算法的精华
{//用检验算子a来检验n是不是素数
ll tem=n-1;
int j=0;
while(tem%2==0)
{
tem/=2;
j++;
}
//将n-1拆分为a^r * s
ll x=q_pow(a,tem,n); //得到a^r mod n
if(x==1||x==n-1) return true;//余数为1则为素数
while(j--) //否则试验条件2看是否有满足的 j
{
x=q_mul(x,x,n);
if(x==n-1)return true;
}
return false;
}
bool miller_rabin(ll n)//检验n是否是素数
{
if(n==2)return true;
if(n<2||n%2==0)return false;//如果是2则是素数,如果<2或者是>2的偶数则不是素数
for(register int i=1;i<=times;i++)//做times次随机检验
{
ll a=Random(n-2)+1;//得到随机检验算子 a
if(!witness(a,n))return false;//用a检验n是否是素数
}
return true;
}
int main()
{
ll x;
while(cin>>x)
{
if(miller_rabin(x))
cout<<"Yes"<<endl;
else
cout <<"No"<<endl;
}
return 0;
}
(话说这个应该归到判断素数的里边啊算了不搬了)
另附:某位大佬在
此算法是
这个算法名字好像叫做
首先放几个定义,定义比较多。。。
根据大佬的但是蒟蒻我根本不会证明。。
然后是
接着是p3的计算方法。
先介绍第一种计算
pi
这种方法的时间复杂度是
为了进一步改善时间复杂度,我们要尽量减少
还是线性筛预处理前
得到:
计算
代码看看就好了。。。。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#pragma warning(disable:4996)
#define INF 2000000005
#define lowbit(a) ((a)&-(a))
#define FAIL -INF
const long long MAXN=6893911;
long long p[MAXN], cnt;
bool mark[MAXN];
int pi[MAXN];
void init()
{
long long i,j;
for (i=2;i<MAXN;i++)
{
if (!mark[i])
p[cnt++]=i;
pi[i]=pi[i-1]+!mark[i];
for (j=0;p[j]*i<MAXN&&j<cnt;j++)
{
mark[p[j]*i]=true;
if (i%p[j]==0)
break;
}
}
}
int f(long long n,int m)
{
if (n==0)return 0;
if (m==0)return n-n/2;
return f(n,m-1)-f(n/p[m],m-1);
}
int Pi(long long N);
int p2(long long n, int m)
{
int ans=0;
for (int i=m+1;(long long)p[i]*p[i]<=n;i++)
ans+=Pi(n/p[i])-Pi(p[i])+1;
return ans;
}
int p3(long long n, int m)
{
int ans=0;
for (int i=m+1;(long long)p[i]*p[i]*p[i]<=n;i++)
ans+=p2(n/p[i],i-1);
return ans;
}
int Pi(long long N)
{
if (N<MAXN)return pi[N];
int lim=f(N,0.25)+1;
int i;
for (i=0;p[i]<=lim;i++);
int ans=i+f(N,i-1)-1-p2(N,i-1)-p3(N,i-1);
return ans;
}
int main()
{
long long L,R;
scanf("%lld %lld",&L,&R);
init();
printf("%d",Pi(R)-Pi(L-1));
return 0;
}
例题:
p3383【模板】线性筛素数
P1865 A % B Problem
可自己练习话说题目很基础啊