Miller-Rabin素数测试法 学习笔记

本文介绍了如何使用Miller-Rabin算法高效地检测质数,通过费马小定理和二次探测定理降低时间复杂度至O(sqrt(n))。详细解析了算法步骤,并给出了C++实现,特别指出在一定范围内错误概率极低,同时提醒注意大整数处理时的溢出问题。

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

一般情况下判断一个数是不是质数时间复杂度是 O ( n ) O(\sqrt{n}) O(n ),用 M i l l e r − R a b i n Miller-Rabin MillerRabin 素数测试法可以降低时间复杂度。

前置知识

1. 1. 1. 费马小定理 : : : p p p 为素数,且 g c d ( a , p ) = 1 gcd(a,p) = 1 gcd(a,p)=1,有 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1\pmod{p} ap11(modp),这是 p p p 为素数的必要不充分条件。

2. 2. 2. 二次探测定理 : : : p p p 为素数,且 0 < x < p 0 < x < p 0<x<p,则方程 x ≡ 1 ( m o d p ) x\equiv1\pmod{p} x1(modp) 的解为 x = 1 x=1 x=1 x = p − 1 x=p-1 x=p1。证明把 1 1 1 移项即可。

M i l l e r − R a b i n Miller-Rabin MillerRabin

2 2 2 以外,所有的 p − 1 p-1 p1 都可以表示为 2 t × u 2^t \times u 2t×u 的形式,则 a p − 1 a^{p-1} ap1 = a 2 t × u a^{2^t \times u} a2t×u = a u 2 t a^{u^{2^t}} au2t。所以我们构造一个 b b b 数组,令 b [ 0 ] = a u b[0] = a^u b[0]=au,只需要每次都平方, b [ i ] = b [ i − 1 ] 2 b[i] = b[i-1]^2 b[i]=b[i1]2,则 b [ t ] = a u 2 t b[t] = a^{u^{2^t}} b[t]=au2t。构造出 b [ i ] b[i] b[i] 之后根据二次探测定理进行判断。如果 b [ i ] = 1 b[i] = 1 b[i]=1,则 b [ i − 1 ] = 1 b[i-1] = 1 b[i1]=1 b [ i − 1 ] = p − 1 b[i-1] = p-1 b[i1]=p1,如果不满足以上的条件,那么 p p p 一定是合数,如果满足了,那么 p p p 很有可能是质数。

由于自乘 k k k 次,最后会得到 a p − 1 a^{p-1} ap1,如果模 p p p 不等于 1 1 1,那么不满足费马小定理,也是合数。

2 , 3 , 5 , 7 , 11 , 13 , 17 2,3,5,7,11,13,17 2,3,5,7,11,13,17 这几个数,在 O I OI OI 范围内出错的概率为 0 0 0

注意会爆 l o n g l o n g long long longlong,可以用快速乘或者是 __ i n t 128 int128 int128

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define int __int128
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
inline void print(int x){
	if(x < 0) putchar('-'),x = -x;
	if(x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}
const int M = 10;
const int mod = 998244353;
const int prime[] = {0,2,3,5,7,11,13,17};
int T,n;
inline int ksm(int x,int y,int p){
	int res = 1;
	while(y){
		if(y&1) res = res*x%p;
		x = x*x%p;
		y>>=1;
	}
	return res%p;
}
inline void work(){
	int n = read();
	if(n == 1) { printf("NO\n"); return; }
	int t = n-1,k = 0;
	while(!(t&1)) k++,t>>=1;
	rep(i,1,7){
		if(n == prime[i]) { printf("YES\n"); return; }
		int base = ksm(prime[i],t,n),now = base;
		rep(j,0,k){
			now = base*base%n;
			if(now == 1 && base^1 && base^(n-1)) { printf("NO\n"); return; }
			base = now;
		}
		if(base^1) { printf("NO\n"); return; }
	}
	printf("YES\n");
	return;
}
signed main(){
	T = read();
	while(T--) work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值