Miller Rabin质数判定模板(2)

这篇博客介绍了Miller-Rabin素数判定方法,基于平方根引理和费马小定理。它阐述了如何通过寻找平方根和检验特定条件来判断一个大于2的奇数是否为素数。如果存在某个a使得测试条件不满足,则可能是合数的凭证,反之可能是素数的‘强伪证’。多次独立测试可以降低误判概率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

首先介绍一个相关的引理

  

  

总是得到1,称这两个数为1的“平凡平方根”。当p是素数且p>2时,不存在

  

的“非平凡平方根”。为了证明该引理,我们假设x是

  

的平方根,于是有 [1] 

推出p|(x+1)(x-1),也就是说,p|x+1或p|x-1。

现在假设n是一个奇素数,且 n>2。于是n-1是一个偶数,可以被表示为

  

的形式,s和d都是正整数且d是奇数。对任意在

  

范围内的a 和

  

,必满足以下两种形式的一种:

因为由于费马小定理,对于一个素数n,有

又由于上面的引理,如果我们不断对

  

取平方根,我们总会得到 1 或 -1。如果我们得到了 -1,意味着②式成立,n是一个素数。如果我们从未得到 -1,那么通过这个过程我们已经取遍了所有2的幂次,即①式成立。

米勒-拉宾素性测试就是基于上述原理的逆否,也就是说,如果如果我们能找到这样一个a,使得对任意

  

以下两个式子均满足:

那么n就不是一个素数。这样的a称为n是合数的一个凭证(witness)。否则a可能是是一个证明n是素数的“强伪证”(strong liar),即当n确实是一个合数,但是对当前选取的a来说上述两个式子均不满足,这时我们认为n是基于a的大概率素数。

每个奇合数n都有很多个对应的凭证a,但是要生成这些a并不容易。当前解决的办法是使用概率性的测试。我们从集合

  

中随机选择非零数a,然后检测当前的a是否是n为合数的一个凭证。如果n本身确实是一个合数,那么大部分被选择的a都应该是n的凭证,也即通过这个测试能检测出n是合数的可能性很大。然而,仍有极小概率的情况下我们找到的a是一个“强伪证”(反而表明了n可能是一个素数)。通过多次独立测试不同的a,我们能减少这种出错的概率。

对于测试一个大数是否是素数,常见的做法随机选取基数a,毕竟我们并不知道凭证和伪证在 [1,n-1]这个区间如何分布。典型的例子是 Arnault 曾经给出了一个397位的合数n,但是所有小于307的基数a都是n是素数的“强伪证”。不出所料,这个大合数通过了 Maple 程序的isprime()函数(被判定为素数)。这个函数通过检测 a=2,3,5,7,11这几种情况来进行素性检验。

//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf_s("%lld", &a)
#define println(a) printf("%lld\n", a)
#define reset(a, b) memset(a, b, sizeof(a))
const int INF = 0x3f3f3f3f;
using namespace std;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1000000007;
const int tool_const = 19991126;
const int tool_const2 = 2000;
inline ll lldcin()
{
	ll tmp = 0, si = 1;
	char c;
	c = getchar();
	while (c > '9' || c < '0')
	{
		if (c == '-')
			si = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
	{
		tmp = tmp * 10 + c - '0';
		c = getchar();
	}
	return si * tmp;
}
///Untersee Boot IXD2(1942)
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
/**Last Remote**/
const int primes[7] = { 2,3,5,31,37,211,317 };
ll quickm(ll a, ll b, ll c)
{
	return ((a*b - ll((ld)a / c * b + 0.005)*c) % c + c) % c;//有风险的快速乘,加上一个数使得它误差减小
}
ll quickp(ll a, ll b, ll c)
{
	ll ans = 1;
	while (b)
	{
		if (b & 1)
			ans = quickm(ans, a, c);
		b >>= 1;
		a = quickm(a, a, c);
	}
	return ans % c;
}
bool Miller_Robin(ll num)
{
	if (num == 2)
		return true;
	if ((num &1)==0)
		return false;
	ll tmp = num - 1,tmp5=0;
	while (!(tmp & 1))
	{
		tmp >>= 1;
		++tmp5;
	}
	for (int i = 0; i < 7; i++)
	{
		if (primes[i] == num)
			return true;
		ll solution = quickp(primes[i], tmp, num);//a^tmp=solution(mod num)
		ll tmpsolution = solution;
		for (int j = 1; j <= tmp5; j++)
		{
			solution = quickm(solution, solution, num);
			if (solution == 1 && tmpsolution != 1 && tmpsolution != num - 1)//如果二次检测原则两个式子都不能满足
				return false;
			tmpsolution = solution;//迭代
		}
		if (solution != 1)//回到了费马小定理逆定理,如果不是1,直接判否
			return false;
	}
	return true;
}
int DETERMINATION()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	ll x;
	while (cin >> x)
	{
		if (x == 1)
			cout << "N" << endl;
		else
		{
			if (Miller_Robin(x))
				cout << "Y" << endl;
			else
				cout << "N" << endl;
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值