费马小定理:若一个数p是素数,a为整数,且gcd(a,p) == 1,则
简单点说就是:假设a为整数,p为素数,且gcd(a,p)==1,那么a的(p-1)次方除以p的余数一定是 1
注意:费马小定理只是素数判定的一个必要条件,素数一定满足费马小定理,满足费马小定理的数,却不一定是素数,例如Carmichael数(Carmichael数都是符合费马小定理的,但是他们都是合数)。
Euler定理:
我们记φ(m)为小于等于m的整数中与m互素的数的个数(这是欧拉函数的定义)。就有:
证明也很简单:这实际上就是费马小定理的一个推广,我们知道当m为素数时,φ(m) = m-1,这是欧拉函数的结论。将φ(m)带入就可以得到费马小定理的结论。
关于欧拉函数可以看这篇blog:
逆元:
这个定理十分有用,我们可以证明一下费马小定理求逆元的做法,我们知道若一个数 M 是素数,则任意整数 A(要求gcd(A, M) == 1) 关于 M 的逆元就是 pow(A, M-2),我们可以证明一下:
求A关于M的逆元,就需要找出满足 A * X ≡ 1 (mod M) 这个式子的 X ,因为 M 为素数,所以 A^(M-1) ≡ 1 (mod M),我们一对比就可找到 X 的一个解 A^(M-2)。
Miller_rabin算法:
我们可以用费马小定理来判断一个很大的数是否是素数,我们知道若一个数p是素数,则必定有 那么我们就可随机枚举出大概10个左右的数,判断这个数的p-1次方对p取模是否为1,但是这样不严谨,因为Carmichael数的存在,虽然Carmichael数的数量很少,但是这也会对算法造成影响。所以在介绍这个算法之前我们再来了解一个定理:二次探测定理,这个定理可以对算法进行改进以免将Carmichael数当做素数。
二次探测定理:如果p是素数,x是小于p的正整数,且
, 则 x = 1 或 x = p-1。
有了二次探测定理我们就可以在使用费马小定理计算a^(p-1) ≡ 1(mod p)时,增加对p的二次探测,一旦发现违背二次探测条件,就可以得出p不是素数的结论。我们通过一个例子来利用这个结论,341是一个合数(341=11*31),但是341可以通过a为2时的费马小定理2^340 ≡ 1(mod 341),我们现在可以假设341为素数:因为(2^170)^2 ≡ 1(mod 341),所以2^170 mod 341只能为1或者是340,我们发现确实有2^170 ≡ 1(mod 341),我们继续往下算2^85 ≡ 32(mod 341),这样一来就可以看出341不是素数了。
这就是Miller_rabin素性测试方法,不断地提取p-1中的因子2,这样我们为了方便可以把p-1表示为d*2^r(d为奇数),这样一来我们就需要不断计算a^(d*2^r)对p取余的结果,这样我们可以强化费马小定理为下面的形式:
尽可能的提取p-1中的因子2,把p-1表示为d*2^r,若p为素数,则a^d mod p = 1,或者存在一个i使得a^(d*2^i) % mod p = p-1 (0 <= i < r)。
需要说明的是,Miller_rabin素性测试也是不确定算法。我们把可以通过以a为底的Miller-Rabin测试的合数称作以a为底的强伪素数。
对于大数的素性判断,目前Miller-Rabin算法应用最广泛。一般底数仍然是随机选取,但当待测数不太大时,选择测试底数就有一些技巧了。比如,如果被测数小于4 759 123 141,那么只需要测试三个底数2, 7和61就足够了。当然,你测试的越多,正确的范围肯定也越大。如果你每次都用前7个素数(2, 3, 5, 7, 11, 13和17)进行测试,所有不超过341 550 071 728 320的数都是正确的。如果选用2, 3, 7, 61和24251作为底数,那么10^16内唯一的强伪素数为46 856 248 255 981。这样的一些结论使得Miller-Rabin算法在比赛中非常实用。通常认为,Miller-Rabin素性测试的正确率可以令人接受,随机选取k个底数进行测试算法的失误率大概为4^(-k)。
以上部分引用自:Matrix67的blog: http://www.matrix67.com/blog/archives/234
现在我们可以给出板子:
1 #include <iostream>
2 #include <algorithm>
3 #include <cstdio>
4 #include <cmath>
5 using namespace std;
6
7 typedef long long LL;
8 typedef unsigned long long ULL;
9
10 inline LL mut_mod(LL a, LL b, LL m){
11 if(b < 0) {
12 a = -a;
13 b = -b;
14 }
15
16 LL ans = 0;
17 a %= m;
18 while(b){
19 if(b&1) ans = (ans+a)%m;
20 b >>= 1;
21 a = (a+a)%m;
22 }
23 return ans;
24 }
25
26 inline LL pow_mod(LL a, LL b, LL m){
27 LL ans = 1;
28 a %= m;
29 while(b){
30 if(b&1) ans = mut_mod(ans, a, m);
31 b >>= 1;
32 a = mut_mod(a, a, m);
33 }
34 return ans;
35 }
36
37 LL primes[] = { 2, 3, 7, 61, 24251};
38
39 bool isprimes(LL n){
40 int r = 0, t;
41 LL x, y, tmp, d = n-1;
42 while((d&1) == 0) d>>=1, r++;
43
44 for(int i = 0; i < 5; i++){
45 tmp = primes[i];
46 if(tmp == n) return true;
47 y = x = pow_mod(tmp, d, n);
48 t = r;
49 while(t--){
50 y = mut_mod(x, x, n);
51 if(y == 1 && x != 1 && x != n-1)
52 return false;
53 x = y;
54 }
55 if(y != 1) return false;
56 }
57 return true;
58 }
59
60 int main(){
61 LL n;
62 while(scanf("%lld", &n) != EOF){
63 if(n > 1 && isprimes(n)) printf("YES\n");
64 else printf("NO\n");
65 }
66 return 0;
67 }
View Code