Miller Rabin算法详解

 


何为Miller Rabin算法

首先看一下度娘的解释(如果你懒得读直接跳过就可以反正也没啥乱用:joy:)

Miller-Rabin算法是目前主流的基于概率的素数测试算法,在构建密码安全体系中占有重要的地位。通过比较各种素数测试算法和对Miller-Rabin算法进行的仔细研究,证明在计算机中构建密码安全体系时, Miller-Rabin算法是完成素数测试的最佳选择。通过对Miller-Rabin 算 法底层运算的优化,可以取得较以往实现更好的性能。[1]  随着信息技术的发展、网络的普及和电子商务的开展, 信息安全逐步显示出了其重要性。信息的泄密、伪造、篡改 等问题会给信息的合法拥有者带来重大的损失。在计算机中构建密码安全体系可以提供4种最基本的保护信息安全的服 务:保密性、数据完整性、鉴别、抗抵赖性,从而可以很大 程度上保护用户的数据安全。在密码安全体系中,公开密钥 算法在密钥交换、密钥管理、身份认证等问题的处理上极其有效,因此在整个体系中占有重要的地位。目前的公开密钥 算法大部分基于大整数分解、有限域上的离散对数问题和椭 圆曲线上的离散对数问题,这些数学难题的构建大部分都需 要生成一种超大的素数,尤其在经典的RSA算法中,生成的素数的质量对系统的安全性有很大的影响。目前大素数的生 成,尤其是随机大素数的生成主要是使用素数测试算法,本 文主要针对目前主流的Miller-Rabin 算法进行全面系统的分析 和研究,并对其实现进行了优化

说白了Miller Rabin算法在信息学奥赛中的应用就一句话:

判断一个数是否是素数

定理

Miller Rabin算法的依据是费马小定理:

ap11(modP)

证明:

性质1:p1个整数a,2a,3a,...(p1)a中没有一个是p的倍数 

性质2:a,2a,3a,...(p1)a中没有任何两个同余与模p

所以a,2a,3a,...(p1)a对模p的同余既不为零,也没有两个同余相同

因此,这p1个数模p的同余一定是a,2a,3a,...(p1)a的某一种排列

a2a3a...(p1)a123...(p1)(modp)

化简为

ap1(p1)!p1!(modp)

根据威尔逊定理可知(p1)!p互质,所以同时约去(p1)!

即得到ap11(modP)

 

那么是不是当一个数p满足任意a使得ap11(modP)成立的时候它就是素数呢?

在费马小定理被证明后的很长一段时间里,人们都觉得这是很显然的,

但是终于有一天,人们给出了反例 ,推翻了这个结论

 

这是否意味着利用费马小定理的思想去判断素数的思想就是错误的呢?

答案是肯定的。

但是如果我们可以人为的把出错率降到非常小呢?

比如,对于一个数,我们有99.99999%的几率做出正确判断,那这种算法不也很优越么?

 

于是Miller Rabin算法诞生了!

 

首先介绍一下二次探测定理

p为素数,a21(modP),那么a±1(modP)

证明

a21(modP)

a210(modP)

(a+1)(a1)0(modP)

那么

(a+1)0(modP)

或者

(a1)0(modP)

(此处可根据唯一分解定理证明)

a±1(modP)

 

这个定理和素数判定有什么用呢?

首先,根据Miller Rabin算法的过程

假设需要判断的数是p

我们把p1分解为2kt的形式

p是素数,有a2kt1(modp)

然后随机选择一个数a,计算出at(modp)

让其不断的自乘,同时结合二次探测定理进行判断

如果我们自乘后的数(modp)=1,但是之前的数(modp)±1

那么这个数就是合数(违背了二次探测定理)

这样乘k次,最后得到的数就是ap1

那么如果最后计算出的数不为1,这个数也是合数(费马小定理)

正确性

老祖宗告诉我们,若p通过一次测试,则p不是素数的概率为25%

那么经过t轮测试,p不是素数的概率为14t

我习惯用2,3,5,7,11,13,17,19这几个数进行判断

在信息学范围内出错率为0%(不带高精)

code

注意在进行素数判断的时候需要用到快速幂。。

这个应该比较简单,就不细讲了

复制代码
#include<cstdio>
#define LL long long 
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int N, M, Test[10] = {2, 3, 5, 7, 11, 13, 17};
int pow(int a, int p, int mod) {
    int base = 1;
    for(; p; p >>= 1, a = (1ll * a * a) % mod) 
        if(p & 1) base = (1ll * base * a) % mod;
    return base % mod;
}
bool Query(int P) {
    if(P == 1) return 0;
    int t = P - 1, k = 0;
    while(!(t & 1)) k++, t >>= 1;
    for(int i = 0; i < 4; i++) {
        if(P == Test[i]) return 1;
        LL a = pow(Test[i], t, P), nxt = a;
        for(int j = 1; j <= k; j++) {
            nxt = (a * a) % P;
            if(nxt == 1 && a != 1 && a != P - 1) return 0;
            a = nxt;
        }
        if(a != 1) return 0;
    }
    return 1;
}
main() { 
    N = read(); M = read();    
    while(M--) puts(Query(read()) ? "Yes" : "No");
}
复制代码

 

posted @   自为风月马前卒  阅读(17235)  评论(8编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库

Contact with me

点击右上角即可分享
微信分享提示