一。筛法
线性时间筛法:
一边制作质数表,一边删掉每个数的质数倍,时间复杂度就能达到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 ;
}