最近在看RSA,找到一个一个大素数是好多加密算法的关键一步,而大素数无法直接构造,一般情况下都是生成一个随机数然后判断是不是素数。判断是否是素数的方法有好多,有的能够准确判断,比如可以直接因式分解(RSA的安全性就基于这是困难的),速度稍微快一点的对素数又有特殊要求,而Miller-Rabin素数检测法可以在一定概率上认为一个数是素数,以极小概率的错误换取时间。Miller-Rabin算法基于一个数如果是素数就满足费马小定理,即a^(n-1) ≡1(mod n),而如果满足此现象却不是素数就成为基于a的伪素数(Carmichael)数,Carmichael数是非常少的。在1~100000000范围内的整数中,只有255个Carmichael数。Miller-Rabin使用多个随机生成的a进行检测就可以将错误率降低到相当低,并且在每次计算模取幂时如果发现对模n来说1的非平凡平方根,就可以提前判断n为素数了。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 5 #define S 50 6 7 int miller_rabin(int n,int s); 8 bool witness(long long base,long long n); 9 10 int main() 11 { 12 int m; 13 while(scanf("%d",&m) != EOF){ 14 int n,cnt = 0; 15 16 for(int i = 0;i < m;i++){ 17 scanf("%d",&n); 18 19 if(n % 2 == 0){ 20 cnt += (n == 2); 21 } 22 else{ 23 cnt += miller_rabin(n,S); 24 } 25 } 26 27 printf("%d\n",cnt); 28 } 29 30 return 0; 31 } 32 33 int miller_rabin(int n,int s) 34 { 35 for(int i = 0;i < s && i < n;i++){ 36 long long base = rand() % (n - 1) + 1; 37 38 if(witness(base,n)){ 39 return 0; 40 } 41 } 42 43 return 1; 44 } 45 46 bool witness(long long base,long long n) 47 { 48 int len = ceil(log(n - 1.0) / log(2.0)) - 1; 49 long long x0 = 0,x1 = 1; 50 51 for(int i = len;i >= 0;i--){ 52 x0 = x1; 53 x1 = (x1 * x1) % n; 54 55 if(x1 == 1 && x0 != 1 && x0 != n - 1){ 56 return true; 57 } 58 if(((n - 1) & (1 << i)) > 0){ 59 x1 = (x1 * base) % n; 60 } 61 } 62 return x1 != 1; 63 } 64 65 //10902607 2014-06-23 23:34:23 Accepted 2138 125MS 228K 946 B G++ 超级旅行者