【概述】
判断素数是一个较常涉及的内容,所谓素性测试是检测一个数是否为素数的测试。
【试除法】
当数据范围较小的情况下,判断素数可从判断 2 到 sqrt(n) 或者 n/2 ,看能不能整除 n,若能整除就不是素数。
bool judge(int n)
{
if(n==1)//为1时,不是
return false;
for(int i=2;i<sqrt(n);i++)//如果能被整除,不是
if(n%i==0)
retrun false;
return true;
}
【埃拉托斯特尼筛法】
对于数据范围较大的情况下,可以使用打表的方法,求出素数表,是一般的线性筛法。
初始时,先假设所有数都是素数,从2开始枚举,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数,把这些合数都筛掉,继续向下枚举,直至所有数枚举完毕。
int prime[N],cnt;
void make_prime(int n)
{
bool bPrime[N];//质数标志数组
cnt=0;//素数个数
memset(bPrime, true, sizeof(bPrime));// 假设全是素数
bPrime[0] = false;// 0: 非素数
bPrime[1] = false;// 1: 非素数
for (int i = 2; i <= n; i++)//枚举2-n的所有数
{
if (bPrime[i])//i是素数
{
prime[cnt++] = i;//将素数i保存
for (int k = i * i; k <= n; k += i)//将素数i的倍数全置为非素数标志
bPrime[k] = false;
}
}
}
【快速线性筛法】
仔细分析埃拉托斯特尼筛法可发现,这种方法会造成重复筛除合数,影响效率。
以 30 为例,在 i=2 的时候,k=2*15 筛了一次;在i=5,k=5*6 的时候又筛了一次。
未避免冗余,提高效率,也就有了快速线性筛法,其可保证不会重复删除一个数。
原理:
① 如果 i 是素数,那么一个大的素数 i 乘以一个不大于 i 的素数,这样筛出的数都是: 形式的,是不会重复的。
② 如果 i 是合数,此时 i 可以表示为递增素数相乘,即:,P1 是最小的系数。
③ 当执行到 p1=prime[j] 的时候,筛选就终止了,也即只能筛选出 不大于 p1 的素数 * i
int prime[N],cnt;
void getPrimes(int n)
{
bool bPrime[N];// 素数标志数组
cnt = 0;// 素数个数
memset(bPrime, true, sizeof(bPrime));// 假设全部为素数
bPrime[0] = false;// 0: 非素数
bPrime[1] = false;// 1: 非素数
for(int i = 2; i <= n; i++)//枚举2-n的所有数
{
if(bPrime[i])//保存素数i
primes[cnt++] = i;
for(int j = 0; j < cnt && i * primes[j] < N; j++)
{
bPrime[i * primes[j]] = false;//置非素数标志
if(i % primes[j] == 0)//i为素数的倍数
break;
}
}
}
【Miller-Rabin 算法】
Miller-Rabin 算法是一个随机算法,随机生成几个 a 利用费马小定理与二次探测定理来检测素数。
只需要多次寻找不超过 n-1 基并检验是否有 , 如果一直有, 那么这个数大概率就是一个素数,否则可以立即判定这个数是个合数。
虽然看似没有问题,但却存在一些数,对于 a 的某些选择可以骗过该算法,这些数虽然不是素数,但却对所有与 p 互素的 0<a<p 满足 ,因此,还需要附加二次探测定理的测试来改进不出错的几率。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 10001
#define MOD 123
#define E 1e-6
using namespace std;
const int cnt=10;
int Pow_Mod(int a, int p, int n)//d≡a^p mod n
{
int res=1;
while(p)
{
if((p&1))
res=(res*a)% n;
if( (a*a)%n==1 && a!=1 && a!=n-1)// 二次探测
return -1;
a=(a*a)%n;
p>>=1;
}
return res;
}
bool Miller_Rabin(int p)
{
if(p==2)//2是素数
return true;
for(int i=1;i<=cnt;i++)
{
int a=rand()%(p-2)+2;//取与p互质的整数a
if(Pow_Mod(a,p-1,p)!=1)
return false;
}
return true;
}
int main()
{
int p;
cin>>p;
srand(time(NULL));//随机种子数
if(Miller_Rabin(p))
cout<<p<<"可能是素数"<<endl;
else
cout<<p<<"是合数"<<endl;
return 0;
}