质数测试——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 素数测试,可以很大概率测出其是否为素数. ...
随机推荐
- Qt开源作品40-图片及文字与base64编码互换
一.前言 对于图片的传输,通俗的做法一般有两种,一种是直接二进制文件传输,比如先传输开始标记,带文件名称.文件字节长度,然后挨个分包发送.最后发送文件结束标记,这种方式必须要求接收方按照自己定义的规则 ...
- Qt通用方法及类库11
函数名 //判断IP地址及端口是否在线 static bool ipLive(const QString &ip, int port, int timeout = 1000); //获取网页所 ...
- 昔日移动端IM明星 “米聊” 即将停止服务
2021年1月19日,小米旗下米聊宣布,将于2021年2月19日12点停止米聊的服务. 1.以下消息来自米聊官网 2.关于米聊 米聊是小米科技出品的一款免费即时通讯工具,推出时间为:2010年12 ...
- OpenMMLab AI实战营 第四课笔记
OpenMMLab AI实战营 第四课笔记 目录 OpenMMLab AI实战营 第四课笔记 目标检测与MMDetection 1.什么是目标检测 1.1 目标检测的应用 1.1.1 目标检测 in ...
- OpenMMLab AI实战营 第三课笔记
OpenMMLab AI实战营 第三课笔记 目录 OpenMMLab AI实战营 第三课笔记 进入 mmclassification 目录 导入工具包 下载数据集 数据集目录结构 下载 config ...
- 【狂神说Java】Java零基础学习笔记-Java基础
[狂神说Java]Java零基础学习笔记-Java基础 Java基础01:注释 平时我们编写代码,在代码量比较少的时候,我们还可以看懂自己写的,但是当项目结构一旦复杂起来,我们就需要用到注释了. 注释 ...
- TagHelper中获取当前Url
在自定义TagHelper时,我们无法通过TagHelperContext 和 TagHelperOutput 获取到当前路由的信息,我们需要添加注入ViewContext [HtmlAttribut ...
- PostGIS数据库操作简介
PostGIS数据库操作简介 PostGIS Docker安装 docker pull postgis/postgis docker run --name postgis -e POSTGRES_PA ...
- 安装Jenkins,并部署vue前端,java后台
Jenkins 安装方式为Docker或war包. Publish Over SSH 插件下架后,Docker部署加ssh远程执行目前没找到合适的替代方案. 我选择的方案为:jenkins和应用服务器 ...
- 教你实现GPUImage【OpenGL渲染原理】
一.前言 本篇主要讲解GPUImage底层是如何渲染的,GPUImage底层使用的是OPENGL,操控GPU来实现屏幕展示 由于网上OpenGL实战资料特别少,官方文档对一些方法也是解释不清楚,避免广 ...