有关素数的各种算法

素数&&算法

素数是个神奇的东西,在数论里经常用到,有很多奇奇怪怪的性质,但是判断一个数是不是素数就是个很麻烦的事情。

1.定义

素数就是质数,两个名词指的是同一种数。 素数的定义是:素数为在大于1的自然数中,除了1和它本身以外不再有其他因数。

和素数相反的就是合数,当然合数也是大于1的,
其中注意两点:

  1. 素数一定大于1,也就是说1不是素数
  2. 偶数只有2是质数,其余都是素数

2.素数判断

① 朴素算法

判断素数最基础的办法就是测试这个数有没有除了1和自身的其他因数,算法实现也很简单,如下:

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

复杂度O(N1/2)

②埃拉托斯特尼筛法

对于判断一定范围内数是否是素数的问题,朴素的算法就要把区间内的每个数都判断一遍,时间复杂度就会变得很高,这种情况下我们可以对区间内的数进行快速预处理,之后判断是否是素数只需要查询区间内的表就行了。
埃式筛法的原理是:一个合数a必定是由一个小于等于a1/2的素数和另一个数相乘得到的。基于此原理,对于区间[2,n]之间的数,我们首先假设区间内所有数都是素数,然后从第一个数开始,如果这个数是质数,就把它的倍数在表中标记为合数,直到找到n1/2为止停止搜索。代码如下:

static const int maxn = 1e6+7;
int isprime[maxn];
void sleve(int n){
    for(int i=1;i<=n;i++) isprime[i] = 1;       //初始化
    isprime[1] = 0;                             //1不是素数
    for(int i=2;i*i<=n;i++){
        if(isprime[i]){                         //如果是素数,把它的倍数标记成合数
            for(int j=i+i;j<=n;j+=i){
                isprime[j] = 0;
            }
        }
    }
}

复杂度O(NloglogN)
例题:http://acm.hdu.edu.cn/showproblem.php?pid=2098
http://acm.hdu.edu.cn/showproblem.php?pid=2136

③线性筛法

线性筛法就是在埃式筛法的基础上略微优化了一下,时间复杂度减少了一点,一般情况下用埃式筛法就够了。线性筛法基于这样的原理:一个合数a必定有它的最小的素因数,我们只要把这个最小素因数的倍数标记成合数就好了。其中需要把n1/2以内的素数给记录下来,代码:

static const int maxn = 1e6+7;
int isprime[maxn];
int prime[maxn];                                //记录素数
void linear_sleve(int n){
    for(int i=1;i<=n;i++) isprime[i] = 1;       //初始化
    isprime[1] = 0;                             //1不是素数
    cnt = 0;                                    //记录当前素数的个数
    for(int i=2;i<=n;i++){
        if(isprime[i]){
            prime[++cnt] = i;
        }
        for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
            isprime[i*prime[j]] = 0;			
            if(i%prime[j]==0) break;            //这时候i已经有因数prime[j]了,也就是说prime[j]*i的最小素因数已经存在在i中了
        }
    }
}

复杂度O(N)

重头戏来了!!!

④Miller——Rabin素数测试

对于上面的几个判断素数的办法,可以解决数量级比较小的素数判断,但是对于数量级很大的素数,n1/2也会超时的这种,就要寻找其他的判断素数的解决方案。
比较常用的就是Miller——Rabin素数测试。因为是测试,所以不能确定这个数就是素数,但是这个数是素数的概率非常高,不是素数的概率几乎为0,小到可以接受。
对于这个算法,首先要知道费马小定理
在p是素数的情况下,对于任意整数a有如下等式成立:
a p a ( m o d   p ) a^{p}≡a(mod \ p)
这就是费马小定理,当a不能被p整除的情况下,费马小定理可以简化成下面的等式:
a p 1 1   ( m o d   p ) a^{p-1} ≡ 1\ (mod \ p)
这个等式表示的时:a的p-1次方对p取模等于1
基于费马小定理我们会想到用费马测试来判断一个数p是否是素数,即:
对于数p,我们取a∈[1,p-1]中的一部分数来对p进行测试,判断ap-1 ≡ 1 (mod p)是否成立,如果通过了所有的测试,那数p就有大概率是个素数,错误率降低到期望以下。
但是实际上存在一些被叫作Carmichael的数,比如561、1105、1729,对于这些数,我们取a∈[1,p-1]之间的所有数对p进行测试,结果每个数都能通过测试。所以费马测试是存在缺陷的。
那么我们就要另寻解决方案。那就是米勒拉宾素数测试
Miller—Rabin素数测试基于二次探测定理

1 &lt; = x &lt; = p 1 p 使 x 2 1   ( m o d   p ) x 1 p 1 1&lt;=x&lt;=p-1 \\ 对于素数p,使得x^2 ≡ 1 \ (mod \ p)成立的x只能是1或者p-1
证明如下:
x 2 1   ( m o d   p ) &lt; = &gt; x 2 1 0   ( m o d   p ) &lt; = &gt; ( x + 1 ) ( x 1 ) 0   ( m o d   p ) x^2 ≡ 1 \ (mod \ p)&lt;=&gt;x^2 - 1 ≡ 0 \ (mod \ p) &lt;=&gt;(x+1)(x-1) ≡ 0\ (mod \ p)
因为p是素数,所以p不能整除x+1也不能整除x-1,所以p只有等于1或者p-1的情况下等式才能成立

现在我们利用 费马小定理 和 二次探测定理 来测试数p是否是素数
首先,我们将2单独判断
其次,对于偶数,则必然不是素数,直接排除。
对于奇数,我们进行多次测试:
首先我们选取一个数x用于对p进行费马测试
如果xp-1≡1 (mod p) 那么p就有可能是素数
根据二次探测定理
如果x(p-1)/2 ≡1 (mod p)或者x(p-1)/2 ≡ p-1 (mod p)成立,那么它的上一层xp-1≡1 (mod p)必然成立
我们先把p-1d·2r 表示(d为奇数,不再有因数2)
我们从d开始不断利用二次探测定理递推,直到d=(p-1)/2为止
如果出现xd·2^r ≡ 1 (mod p)或者xd·2^r ≡ p-1 (mod p),那么就通过了二次探测定理的测试,同时也通过了费马测试。还是直接看代码明白点叭:

long long qpow(long long a,long long b,long long m){		//快速幂取模
    long long ans = 1;
    while(b){
        if(b&1) ans = (ans*a)%m;
        b>>=1;
        a = (a*a)%m;
    }
    return ans;
}
bool Miller_Rabin(int x,int p){					//素数测试
    int d = p-1;
    while(!(d&1)) d>>=1;                        //计算出d
    long long t = qpow(x,d,p);
    while(d!=p-1){
        if(t==1||t==p-1) return true;			//如果算出了t=p-1之后t必定是1
        t = (t*t)%p;
        d<<=1;
    }
    return false;
}
bool isprime(int p){							//素数判断主函数
    if(p<2) return false;
    int a[] = {2,3,5,7,11};						//用于测试的数
    for(int i=0;i<5;i++){
        if(p==a[i]) return true;
        if(!Miller_Rabin(a[i],p)) return false;                   //用a[i]对p进行测试
    }
    return true;                                                  //通过了所有测试,很大概率是素数
}

复杂度O(logN)

猜你喜欢

转载自blog.csdn.net/qq_43710979/article/details/88370788
今日推荐