1.素数的筛法
1.1 素数的普通筛
素数的普通筛就是应用一个素数不能分解成除1和其本身的两数相乘,进而进行素数的标记的做法。用筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列, 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。依次类推,直到筛子为空时结束。如有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
1不是素数,去掉。剩下的数中2最小,是素数,去掉2的倍数,余下的数是:
3 5 7 9 11 13 15 17 19 21 23 25 27 29
剩下的数中3最小,是素数,去掉3的倍数,如此下去直到所有的数都被筛完,求出的素数为:
2 3 5 7 11 13 17 19 23 29
代码实现如下:
void prime_normal(int n){
memset(pflag,true,sizeof(pflag));
pflag[1] = false;
for (int i = 1; i <= n; ++ i){
if (!pflag[i]) continue;
for (int j = 2; j <= n/i ; ++ j){
pflag[ i * j] = false;
}
}
}
然而这种FilterPrime的算法并不是最优O(sqrt(n)log2n)
通过优化可以将其降低至O(n)
1.2 素数的线性筛(欧拉筛法)
我们考虑减少普通筛中多余的部分(例如当 i = 2, j = 9时会筛去18, 而 i = 3, j = 6时也会筛去18,这就增加了时间复杂度),当这个数第一次被其素因子筛去时,就停止后续的筛(保证每个数只被筛一次)。以达到线性筛的目的。
如此,建立pflag进行标记,用prime数组模拟链接表。
代码如下:
bool pflag[maxn];
int prime[maxn];
tot = 0;
void prime_Euler(int x){
for (int i = 2; i <= n; ++ i){
if(!pflag[i]) prime[ ++ tot] = i;
for (int j = 1; j <= tot && prime[j] * i <= maxn; ++ j){
pflag[prime[j] * i] = true;
if ( i % prime[j] == 0) break;
}
}
}
*1.1标记素数为true,而1.2标记素数为false(其实就是懒。。)
2.素数的性质
2.1欧拉函数及证明
性质:
证明:
证明:
将1~n中与n互质的数按顺序排布:x1,x2……xφ(n) (显然,共有φ(n)个数)
我们考虑这么一些数:
m1=a*x1;m2=a*x2;m3=a*x3……mφ(n)=a*xφ(n)
1)这些数中的任意两个都不模n同余,因为如果有mS≡mR (mod n) (这里假定mS更大一些),就有:
mS-mR=a(xS-xR)=qn,即n能整除a(xS-xR)。但是a与n互质,a与n的最大公因子是1,而xS-xR<n,因而左式不可能被n整除。也就是说这些数中的任意两个都不模n同余,φ(n)个数有φ(n)种余数。
2)这些数除n的余数都与n互质,因为如果余数与n有公因子r,那么a*xi=pn+qr=r(……),a*xi与n不互质,而这是不可能的。那么这些数除n的余数,都在x1,x2,x3……xφ(n)中,因为这是1~n中与n互质的所有数,而余数又小于n.
由1)和2)可知,数m1,m2,m3……mφ(n)(如果将其次序重新排列)必须相应地同余于x1,x2,x3……xφ(n).故得出:m1*m2*m3……mφ(n)≡x1*x2*x3……xφ(n) (mod n)
或者说a^[φ(n)]*(x1*x2*x3……xφ(n))≡x1*x2*x3……xφ(n)
或者为了方便:K{a^[φ(n)]-1}≡0 ( mod n ) 这里K=x1*x2*x3……xφ(n)。可知K{a^[φ(n)]-1}被n整除。
但K中的因子x1,x2……都与n互质,所以K与n互质。那么a^[φ(n)]-1必须能被n整除,即a^[φ(n)]-1≡0 (mod n),即a^[φ(n)]≡1 (mod n),得证。
2.3费马小定理
由欧拉定理易证:当a是不能被质数p整除的正整数,则有a^(p-1) ≡ 1 (mod p) p是任意数
其逆命题不一定成立(但在多数情况下,我们认为这是一个必要条件)
2.4素数判定的基本性质
为了防止上述使用费马小定理的逆否命题的误差,我们采用二次探测法进行优化,
它是根据一个定理:如果p是一个素数,那么对于x(0<x<p),若x^2 mod p 等于1,则x=1或p-1。逆否命题:如果对于x(0<x<p),若x^2 mod p 不等于1,则p不是素数。根据这个定理,我们要计算a^(p-1) mod p是否等于1时,可以这样计算,设p-1=(2^t) * k。我们从a^k开始,不断将其平方直到得到a^(p-1),一旦发现某次平方后mod p等于1了,那么说明符合了二次探测定理的逆否命题使用条件,立即检查x是否等于1或p-1,如果不是则可直接判定p为合数。
Miler - Rabbin 算法(概率算法,但在大多条件下认为是真算法)
以下是模板代码:
bool Miller_Rabbin(int n,int a) { //a 属于[2,n)
if (n < 2)return false;
if (!(n%a))return false;
int r = 0, d = n-1;
while (!(d&1)) {
d >>= 1;
r ++;
}
int k = pow_mod( a, d, n);
if (k == 1)return true;
for (int i = 0; i < r; ++ i) {
if (k == n-1)return true;
k = k * k % n;
}
return false;
}