素数筛法和测试

一。筛法

线性时间筛法:

一边制作质数表,一边删掉每个数的质数倍,时间复杂度就能达到O(N) 。

const  int  N  =  20000000 ;
bool  sieve [ N ];
 
void  eratosthenes ()
{
    vector < int >  prime ;
    for  ( int  i = 2 ;  i < N ;  i ++)
    {
        if  (! sieve [ i ])  prime . push_back ( i );
        for  ( int  j = 0 ;  i * prime [ j ]< N ;  j ++)
        {
            sieve [ i * prime [ j ]] =  true ;
            if  ( i  %  prime [ j ] ==  0 )  break ;
        }
    }
}

6n±1 Method

原理是:只拿2 和3 这两个质数先筛过一遍,剩下的数字则用试除法验证是不是质数。

2 和3 的最小公倍数是6 ,我们就把所有数字分为6n 、 6n+1 、 6n+2 、 6n+2 、 6n+3 、 6n+4 、 6n+5 六种( n 是倍率)。除以六会余零的数字为6n ,除以六会余一的数字为6n+1 ,以此类推。

可以看出6n 、 6n+2 、 6n+3 、 6n+4 会是2 或3 的倍数,不属于质数。因此,只要验证6n+1 和6n+5 是不是质数就可以了。( 6n+5 也可以写成6n-1 ,意义不变。)

6n-1 到6n+1 ,再到下一个6n-1 ,再到6n+1 ,把这些要验证的数字由小排到大,可以发现之间的差值会是2 4 2 4 2 4 . .. 不断跳二跳四。实作程式码时,就可以直接用加法加二加四,而不必用乘法及加减法计算6n-1 、 6n+1 ,如此一来程式的执行效率会好一点。

验证的顺序是:数字2 和3 明显的是质数,不必验证;若是从数字5 开始验证,那么下一个要验证的数字就是5+2 ,再下一个就是5+2+4 ,再下一个就是5+2+4+2 ,如此不断下去。

// 以试除法判断质数
bool  isprime ( int  n )
{
    for  ( int  i = 5 ;  i * i <= n ;  i += 2 )
        if  ( n % i  ==  0 )
            return  false ;
    return  true ;
}
 
// 只检查6n±1。这些数字的间隔为2 4 2 4....
void  make_prime ()
{
    cout  <<  "找到质数2" ;
    cout  <<  "找到质数3" ;
    for  ( int  i = 5 ,  gap = 2 ;  i < 1000000 ;  i += gap ,  gap = 6 - gap )
        if  ( isprime ( i ))
            cout  <<  "找到质数"  <<  i ;
}
二。质数测试,测试一个数字是否为质数。
整除性测试法。
依照质数定义,一个质数p 不会被大于1 且小于p 的数字整除,只要把这些数字都拿来试除,就可以判定一个数字是不是质数。

bool  divisibiity_test ( int  n )
{
    // 穷举n所有可能的因数一一试除。
    for  ( int  d = 2 ;  d < n ; ++ d )
        if  ( n  %  d  ==  0 )
            return  false ;    //不是质数
    return  true ;     //是质数
}

bool  divisibiity_test ( int  n )
{
    // 一个数字n不会有大于sqrt(n)的质因数(除了n本身以外)
    int  sqrt_n  =  sqrt ( n );
    for  ( int  d = 2 ;  d <= sqrt_n ; ++ d )
        if  ( n  %  d  ==  0 )
            return  false ;
    return  true ;
}

费玛小定理:

若n是质数,a小于n,则a^n ≡ a (mod n)。
若n是质数,a小于n,a不是零,则a^(n-1) ≡ 1 (mod n)。
费玛质数测试法是运用费玛小定理而想出的方法:

n是质数,费玛小定理一定成立:a^(n-1) % n = 1一定成立。
n是合数,费玛小定理可能成立:a^(n-1) % n = 1可能成立。

当a^(n-1) % n = 1成立,就推定n是质数。
此算法的结果不一定正确。通过测试的数字,可能是合数或质数;无法通过测试的数字,一定是合数。

bool  fermat_primality_test ( int  n )
{
    // 随便取得一个数值当作a,
    // 但是必须大于1、小于n,才有意​​义。
// srand(time(0));
    int  a  =  rand () % ( n - 2 ) +  2 ;
 
    // 计算a^(n-1) % n的值,存于x中。
    int  x  =  1 ;
    for  ( int  i = 1 ;  i < n ; ++ i ) 
        x  =  x  *  a  %  n ;
    return  x  ==  1 ;
// return pow(a, n-1, n) == 1;
}

Miller-Rabin Algorithm
此算法的结果不一定正确。通过测试的数字,可能是合数或质数;无法通过测试的数字,一定是合数。

bool  miller_rabin ( int  n )
{
    if  ( n  <  2 )  return  false ;
 
// srand(time(0));
    int  a  =  rand () % ( n - 2 ) +  2 ;
 
    int  u  =  n - 1 ,  t  =  0 ;
    while  ( u  %  2  ==  0 )  u  >>=  1 ,  t ++;
 
    int  x  =  pow ( a ,  u ,  n );    // x = a ^ u % n;
    if  ( x  ==  1  ||  x  ==  n - 1 )  return  true ;
 
    for  ( int  i = 0 ;  i < t - 1 ;  i ++)
    {
        x  =  mul ( x ,  x ,  n );    // x = x * x % n;
        if  ( x  ==  1 )  return  false ;
        if  ( x  ==  n - 1 )  return  true ;
    }
    return  false ;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值