质数测试——Fermat素数测试和MillerRabin素数测试
质数测试
今天我来填坑了,之前我在数学基础算法——质数篇这篇文章中提到我要单独讲一下MillerRabin算法,最近已经有许多粉丝在催了,所以我马不停蹄的来出这篇文章了,顺便把Fermat素数测试也讲了,因为想要学会 MillerRabin 算法,我们必须要先了解 Fermat算法的问题。
Fermat 素数测试
简介
Fermat prime teat (费马素性检验)是一种素数判定法则,利用随机化算法判断一个数是合数还是可能是素数。但是这样有可能会错。
思路
对于Fermat 素数测试,我们要先知道一个数学上的定理:费马小定理
费马小定理:如果 \(p\) 是一个素数,且整数 \(a\) 不是 \(p\) 的倍数,则 \(a^{p-1} ≡ 1 (modp)\)
根据这个结论,我们又可以稍微转换一下,将其改为 "如果 \(p\) 是一个素数,且整数 \(a\) 为一个小于 \(p\) 且不等于 0 的一个数,则 \(a^{p-1} ≡ 1 (modp)\)"
那么我们就可以知道,当有一个数 \(a\) 使得 \(a^{p-1} \not\equiv 1 (modp)\) 则 \(p\) 一定不是质数。
若我们要检验 \(n\) 是不是质数,那么我们就可以不断选取范围在 \(2 \to n-1\) 中的基数 \(a\) ,并且检验是否每次都有 \(a^{n-1} \equiv 1(mod n)\) 就行了。
代码实现
int quickpow(int a,int b,int x){
	if (b==0)return 1;
	int tmp=quickpow(a,b/2,x)%x;
	tmp=tmp*tmp%x;
	if (b%2==1)tmp*=a%x;
	return tmp%x;
}
bool Fermat(int n){
	if (n<3)return n==2;
	for (int i=1;i<=log2(n);i++){
		int a=rand()%(n-2)+2;
		if (quickpow(a,n-1,n)!=1){
			return false;
		}
	}
	return true;
}
但是这样的测试在遇到一些特殊的数的时候,会错,也就是说满足 \(a^{n-1} \equiv 1(mod n)\) 的 \(n\) 不一定是素数,则这些数被称为以 \(a\) 为底的伪素数。
也就是说,费马小定理的逆定理并不成立,而其中所有与 \(n\) 互质的正整数 \(b\) ,都有同余式 \(b^{n-1} \equiv (mod n)\) 成立,则称合数 \(n\) 为 Carmichael 数,也就是卡迈克尔数。其中最小的一个卡迈克尔数是 561 。
MillerRabin 素数测试
简介
Miller-Rabin prime test(米勒-拉宾素性检验)是一种素数判定法则,利用随机化算法判断一个数是合数还是可能是素数。卡内基梅隆大学的计算机系教授Gary Lee Miller首先提出了基于广义黎曼猜想的确定性算法,由于广义黎曼猜想并没有被证明,其后由以色列耶路撒冷希伯来大学的Michael O. Rabin教授作出修改,提出了不依赖于该假设的随机化算法。
而这个不依赖于该假设的随机化算法就是使用了费马小定理和二次探查定理得到的。
尽管这是MillerRabin素数测试使用了伪素数的概率性测试,但是实际上没有通过了MillerRabin测试,但实际上却是合数。因此我们可以放心使用。
思路
在费马小定理的基础上,我们还要知道一个性质和一个定理。
1.卡迈克尔数的性质
所有的卡迈克尔数没有平方因子并且至少为三个不同的质因子所构成。
例如 \(561= 3 \times 11 \times 17\)。
2.二次探查定理。
二次探查定理:如果 \(p\) 是奇素数,则 \(x^2 \equiv 1(modp)\) 的解为 \(x \equiv 1 (modp)\) 或 \(x \equiv p-1 (modp)\)。
二次探查定理证明(主要是这个比较好证,费马小定理证不来):
\(\because x^2 \equiv 1(modp)\)
\(\therefore (x+1)(x-1) \equiv 0 (modp)\)
\(\therefore x_1=p-1,x_2=1\)
\(\therefore x \equiv 1 (modp),x \equiv p-1 (modp)\)
根据卡迈克尔数的性质,可知其一定不是 \(p^e\) 。
不妨将费马小定理和二次探测定理结合起来使用:
首先将 \(a^{n-1} \equiv 1 (mod n)\) 中的指数 \(n−1\) 分解为 \(n−1=u \times 2^t\) 。
然后在每轮测试中对随机出来的 \(a\) 先求出 \(v = a^{u} \bmod n\),如果此时 \(v=1 或 v=n-1\) ,则与原式等价,所以不管,直接跳过。否则对其进行 \(t\) 次的平方操作。其中如果 \(v\) 等于1,则一定不为素数,如果 \(v\) 等于 \(n-1\) 的话,则有可能是质数[1],所以跳出循环,重新找一个 \(a\) 来计算。这样就得到较为正确的 MillerRabin。
代码实现
#include<bits/stdc++.h>
#define ll __int128
using namespace std;
long long iterations;
// 快速幂函数,计算 a^u % n
ll power(ll o,ll q,ll m){
	if (q==0)return 1;
	ll tmp=power(o,q/2,m);
	tmp=tmp*tmp%m;
	if (q%2==1)tmp*=o%m;
	return tmp%m;
}
// MillerRabin 质数测试函数
bool mb(ll n){
	if (n<3||n%2==0)return n==ll(2);//如果 n 小于 3 或为偶数的话,判断是否为 2
	//否则将 n-1 表示为 2^t * d 的形式,u在最后会变成 d
	ll u=n-1;
	int t=0;
	while (u%2==0){
		u/=2;
		t++;//记录后面每次进行二次探察的次数
	}
	for (int i=1;i<=iterations;i++){
		ll a=2+rand()%(n-4);//选取随机数
		ll v=power(a,u,n);
		if (v==1||v==n-1)continue;
		bool flag=true;//检查是否为合数
		for (int j=1;j<=t;j++){
			v=(v*v)%n;//进行二次探察
			if (v==1)return false;
			if (v==n-1){//有可能为素数
				flag=false;
				break;
			}
		}
		//如果所有测试都未找到 n 是素数的证据,则 n 是合数
		if (flag)return false;
	}
	// 如果所有测试通过,则 n 是素数
	return true;
}
int main(){
	long long k;//中转一下,__int128不能直接输入
	cin>>k;
	iterations=log2(k);
	if (mb(ll(k)))cout<<"Yes";
	else cout<<"No";
	return 0;
}
时间复杂度的分析
1.power函数的时间复杂度
power函数用于计算幂次模运算 a^u % n。
由于使用的是快速幂,所以power 函数的时间复杂度为 \(O(log_2r)\),这里的 u 是函数参数中传入的幂次值。
2.MillerRabin函数的时间复杂度
- 分解 
n - 1为 \(2^t \times d\) 的形式:
通过不断将u(初始值为n - 1)除以 2,直到r变为奇数。这个过程的时间复杂度取决于n - 1中 2 的因子个数。在最坏情况下,n - 1可能是 2 的幂次方,此时需要执行 \(O(log_2(n-1))\) 次除法操作。但通常情况下,这个操作的时间复杂度相对较小,可以近似看作常数时间。 - 迭代测试部分:函数进行了 
iterations次迭代测试。在每次迭代中: - 随机选择 
a的操作可以看作是常数时间。 
- 随机选择 
 - 调用 
power函数计算 \(x = a^r \bmod n\),根据前面的分析,其时间复杂度为 \(O(log_2n)\) ,这里的r是经过分解n - 1得到的奇数部分(在循环开始前计算得到),可以近似看作 \(O(log_2n)\)。 
- 调用 
 - 后续的 
for循环用于重复平方检查,每次循环也执行了一些常数时间的操作(模运算、乘法和条件判断等),循环的次数最多为 \(O(log_2n)\),因为 \(t=log_2(n)\),其大小与n相关,所以整个for循环的时间复杂度为 \(O(log_2n)\) 
- 后续的 
 
综合起来,每次迭代的时间复杂度主要由调用 power 函数和 for 循环决定,大致为 \(O(log_2n)\)。由于进行了 iterations 次迭代,而iterations的值为 \(log_2n\) ,所以 millerRabin 函数的这部分时间复杂度为 \(O((log_2n)^2)\) ,所以整个程序的时间复杂度为 \(O((log_2n)^2)\) 。
不难发现这个时间复杂度在大数据的时候是要比 \(O(\sqrt n)\) 要快的多的。
正确性说明
Miller-Rabin 算法通过多次随机选取不同的进行测试,可以不断提高判断的准确性。虽然对于单个 \(a\) 的测试可能会将合数误判为素数(存在一定的误判概率),但当进行足够多次的测试(例如 \(k\) 次)后,误判的概率会急剧下降。具体来说,对于一个合数 \(n\),经过 \(k\) 次独立的 Miller-Rabin 测试后,误判为素数的概率不超过 \(\frac{1}{4^k}\)。当取一个合适的值(如 \(k=20\))时,这个误判概率已经非常小,可以在实际应用中满足对素数判断的准确性要求。
当计算到 \(v == n - 1\) 时,这意味着在当前的迭代步骤中,我们得到了一个特殊的值。从费马小定理相关的推导角度来看,我们是在逐步计算 \(a\) 的幂次对 \(n\) 取模的值(这里的 \(v\) 就相当于在计算过程中的某个幂次取模后的结果)。
如果在某个阶段得到 \(x == n - 1\),这与 \(a^{n - 1} ≡ 1 (mod n)\) 的情况存在一定联系。因为 \(n - 1\) 对 \(n\) 取模的结果本身就是 \(n - 1\),而当 \(x\) 达到 \(n - 1\) 时,它有可能是在朝着满足 \(a^{n - 1} ≡ 1 (mod n)\) 这个条件的方向发展的中间一个提前呈现出部分特征的一个中间状态(虽然还不能确定最终一定能满足,但有这种可能性)。
所以当出现 \(x == n - 1\) 时,我们不能就此判定 \(n\) 是合数,而是认为 \(n\) 仍然有是素数的可能,于是将表示是否为合数的标志 \(flag\) 设置为 \(false\),继续进行后续的测试步骤,以便进一步考察 \(n\) 是否真的满足素数的相关条件。
例如,假设 \(n - 1 = 2^3 * d\) ,在迭代过程中,可能在进行了第一次或第二次平方取模操作后(也就是 \(t\) 还没有到达 3 的时候),\(v\) 就已经等于 \(n - 1\) 了。虽然此时还不能确定最终完整经过 \(3\) 次平方取模操作后一定能满足 \(a^{n - 1} ≡ 1 (mod n)\),但这个 \(v == n - 1\) 的情况表明在当前的计算路径上,已经出现了与最终目标条件可能相关的一种状态,所以不能就此判定 \(n\) 为合数,而是要继续进行后续的迭代测试,看最终是否能真正满足 \(a^{n - 1} ≡ 1 (mod n)\) 这个关键条件来判断 \(n\) 的素性。 ︎
质数测试——Fermat素数测试和MillerRabin素数测试的更多相关文章
- RSA,Miller-Rabin素数测试的源流及其证明
		
一.RSA与公钥加密系统的起源与影响. 为了更好地突出公钥加密系统相对私钥加密系统的优势,让我们从这两个问题开始: 这个世界上如果没有公钥加密系统会怎么样呢?全用私钥加密系统会出现什么问题呢? 首先, ...
 - 【数学】【筛素数】Miller-Rabin素性测试 学习笔记
		
Miller-Rabin是一种高效的随机算法,用来检测一个数$p$是否是素数,最坏时间复杂度为$\log^3 p$,正确率约为$1-4^{-k}$,$k$是检验次数. 一.来源 Mil ...
 - 初学MillerRabin素数测试
		
前言 \(MillerRabin\)素数测试是一种很实用的素数判定方法. 它只针对单个数字进行判定,因而可以对较大的乃至于\(long\ long\)范围内的数进行判定,而且速度也很快,是个十分优秀的 ...
 - Miller-Rabin素数测试
		
Miller-Rabin素数测试 给出一个小于1e18的数,问它是否为质数?不超过50组询问.hihocoder 我是真的菜,为了不误导他人,本篇仅供个人使用. 首先,一个1e18的数,朴素\(O(\ ...
 - POJ Pseudoprime numbers( Miller-Rabin素数测试 )
		
链接:传送门 题意:题目给出费马小定理:Fermat's theorem states that for any prime number p and for any integer a > 1 ...
 - Miller-Rabin素数测试算法
		
\(Miller-Rabin\)素数测试 用途 判断整数\(n\)是否是质数,在\(n\)较小的情况下,可以使用试除法,时间复杂度为\(O(\sqrt n)\).但当\(n\)的值较大的时候,朴素的 ...
 - Miller-Rabin素数测试算法
		
用来干嘛的  要判断一个数 \(n\) 是否为素数,最朴素直接的办法是以\(O(\sqrt n)\) 时间复杂度地从2到 \(\sqrt n\) 循环即可得到最准确的结果.但是如果在 \(n\) ...
 - Miller-Rabin素数测试算法(POJ1811Prime Test)
		
题目链接:http://poj.org/problem?id=1811 题目解析:2<=n<2^54,如果n是素数直接输出,否则求N的最小质因数. 求大整数最小质因数的算法没看懂,不打算看 ...
 - Miller-Rabin 素性测试 与 Pollard Rho 大整数分解
		
\(\\\) Miller-Rabin 素性测试 考虑如何检验一个数字是否为素数. 经典的试除法复杂度 \(O(\sqrt N)\) 适用于询问 \(N\le 10^{16}\) 的时候. 如果我们要 ...
 - Miller-Rabin素性测试
		
有时候我们想快速的知道一个数是不是素数,而这个数又特别的大导致 $O(\sqrt n)$ 的算法也难以通过,这时候我们可以对其进行 Miller-Rabin 素数测试,可以很大概率测出其是否为素数. ...
 
随机推荐
- Spring Boot logback springProperty 设置默认值
			
springProperty 当没有读取到source字段中设置的log.path值时,设置为defaultValue字段中的${user.dir}/logs变量值. <springProper ...
 - 2019.12.10笔记——Spring Boot热部署的使用和实现自己的热部署(类加载器相关)
			
Spring Boot热部署 热部署的使用 引入依赖 <!-- spring boot热部署的依赖 --> <dependency> <groupId>org.sp ...
 - WebSocket从入门到精通,半小时就够!
			
本文原题"WebSocket:5分钟从入门到精通",作者"程序猿小卡_casper",原文链接见文末参考资料部分.本次收录时有改动. 1.引言 自从HTML5里 ...
 - OpenMMLab AI实战营 第二课笔记 计算机视觉之图像分类算法基础
			
OpenMMLab AI实战营 第二课笔记 目录 OpenMMLab AI实战营 第二课笔记 图像分类与基础视觉基础 1.图像分类问题 1.1 问题的数学表示 1.2 视觉任务的难点 1.2.1 超越 ...
 - 关于vue加element-ui上传文件获取文件的sha256的值
			
首先使用element的上传文件的组件 安装依赖crypto-js npm i crypto-js <el-upload class="upload-demo" drag : ...
 - 谈谈flutter的线程
			
本文同步发布于公众号:移动开发那些事谈谈flutter的线程 刚接触flutter的同学肯定会对fluter所谓的单线程架构很蒙逼,因为这与我们学开发时,各种语言里的多线程的介绍有点出入,而且手机的C ...
 - Python库房管理系统开发指南
			
在现代仓储管理中,高效.准确的信息系统是提高运营效率的关键.Python作为一种强大且易于学习的编程语言,非常适合用来开发简易而功能齐全的库房管理系统.本文将详细介绍如何使用Python编写一个基本的 ...
 - 阿里云-数据库-表格存储Tablestore
			
入门篇一 初步调研了解 Step.1 场景锲合度判断选择使用表格存储前关键需要明确你的场景是否适合.表格存储是阿里云自2010起自研使用的一个多模型NoSQL数据库,面向海量大数据存储,身经百战.非常 ...
 - 分布式文件系统KFS基础知识介绍
			
Kosmos distributed file system,简称KFS,是一个类GFS的分布式文件系统,被设计用于分布式的结构化存储.下面将对KFS的体系结构进行简单介绍,最后给出一个使用KFS C ...
 - Docker离线部署Nginx
			
总体思路:在有网络的环境上制作Nginx的镜像包,导出并上传至无网络的环境上,启动Nginx即可. 在上一篇 <无网环境Docker Rpm离线安装> 里面,已经在联网的机器上安装好 ...