素性测试

【概述】

判断素数是一个较常涉及的内容,所谓素性测试是检测一个数是否为素数的测试。

【试除法】

当数据范围较小的情况下,判断素数可从判断 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 的素数,这样筛出的数都是:n=p_1*p_2 形式的,是不会重复的。

② 如果 i 是合数,此时 i 可以表示为递增素数相乘,即:i=p_1*p_2*...*p_n,2\leqslant i\leqslant n,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-1} \equiv 1(mod\:\, p), 如果一直有, 那么这个数大概率就是一个素数,否则可以立即判定这个数是个合数。

虽然看似没有问题,但却存在一些数,对于 a 的某些选择可以骗过该算法,这些数虽然不是素数,但却对所有与 p 互素的 0<a<p 满足 a^{p-1} \equiv 1(mod\:\, 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;
}

猜你喜欢

转载自blog.csdn.net/u011815404/article/details/81325070