判断质数 _ Miller Rabin算法

原文1

原文1

算法作用

判断一个数是否是素数

算法依据

费马小定理

如果P是素数,且整数a不是p的倍数有:

\[a^{p-1}\equiv 1 \pmod P \]

费马定理只是n是素数的必要条件。即费马定理不成立,n一定是合数;费马定理成立,n可能是素数。

点击查看证明

性质 1:$p-1$个整数 $a,2a,3a,...(p-1)a$中没有一个是 $p$的倍数 

性质 2:$a,2a,3a,...(p-1)a$中没有任何两个同余与模 $p$的

所以 $a,2a,3a,...(p-1)a$对模 $p$的同余既不为零,也没有两个同余相同

因此,这 $p-1$个数模 $p$的同余一定是 $a,2a,3a,...(p-1)a$的某一种排列

即 $a*2a*3a*...*(p-1)a \equiv {1*2*3*...*(p-1)} \pmod p$

化简为

$a^{p-1}*(p-1)! \equiv {p-1}! \pmod p$

根据威尔逊定理可知 $(p-1)!$与 $p$互质,所以同时约去 $(p-1)!$

即得到 $a^{p-1}\equiv 1 \pmod P$

二次探测定理

\(p\)为素数,对于:

\[a^{2}\equiv 1 \pmod P \]

求解的解只有两种可能:a = 1 或者 a = p-1

点击查看证明

$a^{2}\equiv 1 \pmod P$

$a^{2}-1\equiv 0 \pmod P$

$(a+1)*(a-1)\equiv 0 \pmod P$

那么

$(a+1)\equiv 0 \pmod P$

或者

$(a-1)\equiv 0 \pmod P$

(此处可根据唯一分解定理证明)

即

$a\equiv \pm 1 \pmod P$
$a^{2}\equiv 1 \pmod P$

$a^{2}-1\equiv 0 \pmod P$

$(a+1)*(a-1)\equiv 0 \pmod P$

那么

$(a+1)\equiv 0 \pmod P$

或者

$(a-1)\equiv 0 \pmod P$

(此处可根据唯一分解定理证明)

即

$a\equiv \pm 1 \pmod P$

推理定理

Miller Rabin 算法的过程

假设需要判断的数是 \(p\)

我们把 \(p-1\)分解为 \(2^k*t\)的形式

\(p\)是素数,有 \(a ^ {2^k * t} \equiv 1 \pmod p\)

然后随机选择一个数 \(a\),计算出 \(a^t \pmod p\)

让其不断的乘 \(a\),同时结合二次探测定理进行判断

如果

\[自乘后的数\pmod p = 1 \]

那么这个数就是合数 (违背了二次探测定理)

假如p是素数,那么自乘后的数应该== 1或n-1

这样乘 \(k\)次,最后得到的数就是 \(a^{p-1}\)

那么如果最后计算出的数不为 \(1\),这个数也是合数 (费马小定理)

正确性判断

老祖宗告诉我们,若 \(p\)通过一次测试,则 \(p\)不是素数的概率为 \(25\)%

那么经过 \(t\)轮测试,\(p\)不是素数的概率为 \(\dfrac {1}{4^{t}}\)

我习惯用 \(2,3,5,7,11,13,17,19\)这几个数进行判断

在信息学范围内出错率为 \(0\)%(不带高精)

算法流程

  1. 首先对于要判断的数 n,先判断是不是 2,是就直接返回 true。
  2. 判断是不是小于 2 或者为偶数,是就返回 false。
  3. 令 n-1 = m*2t,求出 u 和 t。
  4. 利用 rand 随机找一个 a, a 属于 [1, n-1]
  5. 令 x = (am)%n
  6. 然后进行 t 次循环,为什么要循环 t 次呢,上面证明是从 2t 倒着推到 20,我们写程序完全可以正着来,上面的 x 其实就是
  7. 那么在循环中每次都进行 y =(x*x)%n, x = y 的操作,最终会变为 an-1,也就是

    又因为 x 在循环时是一定小于 n 的,所以可以用二次探测定理
    运算过程中如果 (x2)%n = 1, 也就是 y = 1,假如 n 是素数,那么 x == 1 或者 x == n-1,否则 n 就一定不是素数,直接返回 false。
  8. 整个循环结束后,程序运行到最后 x = an-1, 根据费马小定理,如果 x 还是不等于 1,那么肯定不是素数,直接返回 false。
  9. Miller-Rabin 算法正确率为 75%,所以要进行大约 4~8 次来提高正确率。

代码

#include <iostream>
#include <cstdlib> 

using namespace std;

typedef long long ll;

ll qul_mul(ll a, ll b, ll n)  //快速积运算,快速求出a^m^*a^m^并且能防止爆掉
{
	ll num = 0;
 
 	while (b)
	{
		if (b&1)
		{
			num=(num+a)%n;	
		}
	  
		a = (a+a)%n;
		b>>=1;
	}
 
	return num;
}

ll qul_pow(ll a, ll b, ll n) //快速幂计算快速求出a^m^的值
{
	ll sum = 1;
 
	while (b)
	{
		  if (b&1)
		  {
		  	sum = sum * a % n;
		  }
		  
		  a=a*a%n;
		  b>>=1;
	}
		 
	 return sum;
}

bool Miller_Rabin(ll n)
{
	int m = n-1;
	int t = 0;
 
	if (n==2)
	{
 		return true;
	}
	else if (n<2 || !(n&1))
	{
 		return false;
	}
 
 	//求出n-1 = m*2^t的m和t。
	while (!(m&1))
	{
		m>>=1;
		t++;
	}
 
	for (int i=0; i<20; i++)
	{    
		//随机数a
		ll a = rand()%(n-1) + 1;
		// 计算a^m
		ll x = qul_pow(a, m, n), y;
  		
		for (int j=0; j<t; j++)
		{
			y = qul_mul(x, x, n);  //进行(x*x)%n操作。
   			
   			//不满足二次探测定理,也就是y得1了但是x并不等于1或者n-1,那么n就一定不是质数。
			if (y==1 && x!=1 && x!=n-1)
			{
 				return false;
			}
   
			x = y;
		}
  		//上面循环跑完了,如果最后x都不等于1,那么一定是一个合数了。
		if (x!=1)
		{
			return false;
		}
	}
 
	return true;
}

int main()
{
	ll n;
 
	while (cin >> n)
	{
		cout << Miller_Rabin(n) << endl;
	}
 
	return 0;
}


代码

注意在进行素数判断的时候需要用到快速幂。。

#include<cstdio>
#define LL long long 
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int N, M, Test[10] = {2, 3, 5, 7, 11, 13, 17};
int pow(int a, int p, int mod) {
    int base = 1;
    for(; p; p >>= 1, a = (1ll * a * a) % mod) 
        if(p & 1) base = (1ll * base * a) % mod;
    return base % mod;
}
bool Query(int P) {
    if(P == 1) return 0;
    int t = P - 1, k = 0;
    while(!(t & 1)) k++, t >>= 1;
    for(int i = 0; i < 4; i++) {
        if(P == Test[i]) return 1;
        LL a = pow(Test[i], t, P), nxt = a;
        for(int j = 1; j <= k; j++) {
            nxt = (a * a) % P;
            if(nxt == 1 && a != 1 && a != P - 1) return 0;
            a = nxt;
        }
        if(a != 1) return 0;
    }
    return 1;
}
main() { 
    N = read(); M = read();    
    while(M--) puts(Query(read()) ? "Yes" : "No");
}
posted @ 2022-02-22 20:42  kingwzun  阅读(283)  评论(0编辑  收藏  举报