费马小定理:若一个数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:https://www.cnblogs.com/DynastySun/p/9364673.html
逆元:
这个定理十分有用,我们可以证明一下费马小定理求逆元的做法,我们知道若一个数 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)。
关于逆元可以看这篇blog:https://www.cnblogs.com/DynastySun/p/9362505.html
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 }