【LOJ#143】质数判定

题目

题目链接:https://loj.ac/p/143
给定一个数 (n),判断是否是质数。
(nleq 10^{18})

思路

Miller–Rabin 素数测试板子题。推荐博客
质数有两个性质。一是众所周知的费马小定理:若 (p) 是质数,且 (x) 不是 (p) 的倍数,则

[x^{p-1}equiv 1pmod p ]

还有一个二次探测定理。如果 (p) 是质数且 (x^2equiv 1pmod p),那么 (xequiv1pmod p)(xequiv p-1pmod p)
证明直接把 (1) 移项平方差即可。
所以对于一个要测试的数 (p),我们取一个数 (a),先判断 (a^{p-1}mod p) 是否等于 (1)。如果不是那么显然 (p) 是合数。
接下来重复此操作:

  1. 如果 (p-1) 是奇数则直接退出循环。
  2. 计算 (a^{frac{p-1}{2}}mod p),根据二次探测定理,如果答案不是 (1)(n)(p) 是合数。
  3. 如果答案是 (1) 则取 (p'=frac{p-1}{2}) 继续探测。

随机选 (a) 的话,Miller–Rabin 有 (frac{1}{4}) 的概率出错。所以如果测试 (k) 次,测试错误的概率就降至 (frac{1}{4^k})
在数据范围不同时 (a) 的选取可以参考 wiki

代码

#include <bits/stdc++.h>
using namespace std;
typedef long double ld;
typedef long long ll;

const int prm[10]={0,2,3,5,7,11,13,17,19,23};
ll n;

ll fmul(ll x,ll y,ll MOD)
{
	ll z=(ld)x*y/MOD;
	return ((x*y-z*MOD)%MOD+MOD)%MOD;
}

ll fpow(ll x,ll k,ll MOD)
{
	ll ans=1;
	for (;k;k>>=1,x=fmul(x,x,MOD))
		if (k&1) ans=fmul(ans,x,MOD);
	return ans;
}

bool MR(ll n)
{
	if (n==1) return 0;
	for (int i=1;i<=9;i++)
	{
		if (n==prm[i]) return 1;
		if (n<prm[i]) return 0;
		ll m=n-1,power=fpow(prm[i],m,n);
		while (power==1 && !(m&1))
			m>>=1,power=fpow(prm[i],m,n);
		if (power!=1 && power!=n-1) return 0;
	}
	return 1;
}

int main()
{
	while (scanf("%lld",&n)!=EOF)
		if (MR(n)) printf("Y
");
			else printf("N
");
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14296950.html