Miller_Rabbin算法判断大素数

普通的素数测试我们有O(√ n)的试除算法。事实上,我们有O(s*log³n)的算法。

 

下面就介绍一下Miller_Rabbin算法思想:

定理一:假如p是质数,且(a,p)=1,那么a^(p-1)≡1(mod p)。即假如p是质数,且a,p互质,那么a的(p-1)次方除以p的余数恒等于1。(费马小定理)

定理二:如果p是一个素数,那么对于x(0<x<p),若x^2 mod p 等于1,则x=1或p-1。

 

它利用了费马小定理,即:如果p是质数,且a,p互质,那么a^(p-1) mod p恒等于1。也就是对于所有小于p的正整数a来说都应该复合a^(p-1) mod p恒等于1。那么根据逆否命题,对于一个p,我们只要举出一个a(a<p)不符合这个恒等式(就是a^(p-1) mod p恒等于1式子),则可判定p不是素数。Miller-rabin算法就是多次用不同的a来尝试p是否为素数。

 

但是每次尝试过程中还做了一个优化操作,以提高用少量的a检测出p不是素数的概率。这个优化叫做二次探测。它是根据一个定理:如果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为合数。

 

一些问题:

1、对于p-1=(2^t) * k这个t的找法,可以记录一下x(x表示(p-1))的二进制形式下末尾有多少0,比如二进制(1000)B代表的是十进制(8)D,8的二进制数形式下末尾0有3个,那么也就是8=(2^3)*1;再例如二进制(11000)B代表的是十进制(24)D,24的二进制数形式下末尾0有3个,那么也就是24=(2^3)*3

 

2、对于随机数a的生成,因为我们这个算法判断的是p这个数是不是素数,那么素数和任意一个数都互质,题目又有要求a<p所以就随机生成一个[1,p-1]范围内的数就行

 

3、这个算法的复杂度是O(s*log³n),这个s是我们提前定好的,因为上面说了我们用的是费马小定理的逆否命题,但是该定理的逆命题是不一定成立的,但是令人可喜的是大多数情况是成立的。所以我们可以多找几个数a来验证一下p是否是素数,找几个数,这个多少数就是s

 

可以证明,使用以上两个定理以后,检验s次出错的概率至多为2^(-s),所以这个算法是很可靠的。

 

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<map>
 7 #include<vector>
 8 #include<math.h>
 9 #define mem(a,x) memset(a,x,sizeof(a))
10 using namespace std;
11 typedef long long LL;
12 const int maxn=50005;
13 const int mod=26;
14 const int INF=0x3f3f3f3f;
15 const int Times = 10;
16 const int N = 5500;
17 LL ct, cnt;
18 LL fac[N], num[N];
19 LL gcd(LL a, LL b)  //求两数最大公因子
20 {
21     return b? gcd(b, a % b) : a;
22 }
23 LL multi(LL a, LL b, LL m)  //快速乘
24 {
25     LL ans = 0;
26     a %= m;
27     while(b)
28     {
29         if(b & 1)
30         {
31             ans = (ans + a) % m;
32             b--;
33         }
34         b >>= 1;
35         a = (a + a) % m;
36     }
37     return ans;
38 }
39 LL pow(LL a, LL b, LL m)  //快速幂
40 {
41     LL ans = 1;
42     a %= m;
43     while(b)
44     {
45         if(b & 1)
46         {
47             ans = multi(ans, a, m);
48             b--;
49         }
50         b >>= 1;
51         a = multi(a, a, m);
52     }
53     return ans;
54 }
55 bool Miller_Rabin(LL n)  //判断是不是素数
56 {
57     if(n == 2) return true;
58     if(n < 2 || !(n & 1)) return false;
59     LL m = n - 1;
60     int k = 0;
61     while((m & 1) == 0)
62     {
63         k++;  //这个k就是我们讲的时候的t
64         m >>= 1;  //这个m就是k
65     }
66     for(int i=0; i<Times; i++) //Times就是我们事先定义的s(要找a的个数)
67     {
68         LL a = rand() % (n - 1) + 1;  //找一个[1,n-1]内的任意数
69         LL x = pow(a, m, n);
70         LL y = 0;
71         for(int j=0; j<k; j++)
72         {
73             y = multi(x, x, n);
74             if(y == 1 && x != 1 && x != n - 1) return false;
75             x = y;
76         }
77         if(y != 1) return false;
78     }
79     return true;
80 }
81 int main()
82 {
83     LL n;
84     while(cin>>n)
85     {
86         if(Miller_Rabin(n))
87             printf("是素数\n");
88         else printf("不是素数\n");
89     }
90     return 0;
91 }

 

posted @ 2020-04-09 15:47  kongbursi  阅读(1155)  评论(0编辑  收藏  举报