Miller-Rabin 与 Pollard-Rho

news/2024/9/18 8:53:23/文章来源:https://www.cnblogs.com/dzbblog/p/18377127

1 Miller-Rabin 算法

1.1 引入

Miller-Rabin 的主要作用就是判断一个较大的数是不是质数。

那么根据基础数论中提到过的试除法,我们知道朴素去判断一个数是否是质数的复杂度是 \(O(\sqrt n)\) 的,在 \(n\ge 10^{18}\) 的时候就十分不优了。

而 Miller-Rabin 则是基于费马小定理进行的素性测试,所以首先我们需要知道费马小定理是什么:

\(p\) 为质数时,对于任意整数 \(a\),会有 \(a^{p-1}\equiv 1\pmod p\)

那么如果对于所有的 \(a\),都有 \(a^{p-1}\equiv 1\pmod p\),是否说明 \(p\) 一定是质数呢?事实上不一定,但是其正确率仍然是有的。那我们如果可以利用这种随机性,把正确率不断提高逼近 \(100\%\),是否说明这样的做法也有一定的可取性?

事实上本文介绍的两个算法看上去都是基于随机的,但是都有很高的正确率。

1.2 算法实现

在了解 Miller-Rabin 的实现过程之前,需要了解二次探测定理。

1.2.1 二次探测定理

二次探测定理:若 \(p\) 为素数,且 \(a^2\equiv 1\pmod p\),那么一定有 \(a\equiv \pm1\pmod p\)

证明:

\(\because a^2\equiv 1\pmod p\)

\(\therefore a^2-1\equiv 0\pmod p\)

\(\therefore (a+1)(a-1)\equiv 0\pmod p\)

\(\therefore\) \(a+1\equiv 0\pmod p\)\(a-1\equiv 0\pmod p\)

1.2.2 算法流程

加入我们当前要判断的数是 \(p\),我们考虑将 \(p-1\) 分解为 \(2^{k}\times t\) 的形式(显然一定可以分解出来)。当 \(p\) 是质数时,一定有 \(a^{2^{k}\times t}\equiv 1\pmod p\)

那么我们就算出 \(a^t\) 的值,然后不断自乘去累加前面的 \(2^k\) 这一部分的指数。在自乘过程中利用二次探测定理进行判断,如果这一次自乘的数 \(\bmod p=1\),但是上一次的数 \(\bmod p\ne \pm 1\),那么这个数就是合数。同时最后自乘完还要利用费马小定理再判断一次。

那么现在的问题就只在于 \(a\) 的选择,事实上 \(a\) 可以取随机数,也可以取一些较小的质数,但是测试的轮数需要足够,一般取到 \(10\) 左右。

代码如下:

int prim[15] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};bool Miller_Rabin(int x) {if(x == 2) return 1;int t = x - 1, k = 0;while(!(t & 1)) t >>= 1, k++;for(int i = 1; i <= 13; i++) {if(x == prim[i]) return 1;int a = qpow(prim[i] % x, t, x), nxt;for(int j = 1; j <= k; j++) {nxt = a * a % x;if(nxt == 1 && a != 1 && a != x - 1) return 0;a = nxt;}if(a != 1) return 0;}return 1;
}

2 Pollard-Rho 算法

2.1 引入

首先考虑这样一个问题:给定一个正整数 \(N\),找出它的一个非平凡因子(除 \(1\)\(N\) 以外的因子)。

我们在基础数论中提到过可以利用试除法来求解,只需要枚举所有 \([1,\sqrt N]\) 中的数即可,复杂度是 \(O(\sqrt N)\) 的。但是如果 \(N\ge 10^{18}\),这个复杂度无疑是十分差劲的,那么有没有更快的算法呢?

这个时候考虑我们的玄学方法,也就是随机化。我们在 \([1,\sqrt N]\) 里面随便猜一个数,显然其效率是 \(O(1)\) 的,不过正确率就降到了 \(10^{-18}\)​。那么有没有方式提高猜测的正确率呢?

事实上有,而这就是 Pollard-Rho 的基本思想。

2.2 生日悖论

首先我们需要引入一个东西叫做生日悖论。

我们考虑这样一个问题:假如一个房间中有 \(k\) 个人,那么当 \(k\) 达到多少时,其中两个人生日相同(不考虑闰年)的概率可以达到 \(50\%\)

我们考虑正难则反,显然 \(k\) 个人生日互不相同的概率是 \(P=\dfrac{365}{365}\times\dfrac{364}{365}\times\dfrac{363}{365}\times\cdots\times\dfrac{365-k+1}{365}\)。也就是说生日有重复的概率 \(P'=1-\prod\limits_{i=1}^{k}\dfrac{365-i+1}{365}\)。实际上,当我们令 \(P'\ge \dfrac 12\) 时,可以解得 \(k\) 大概只要取到 \(23\) 以上即可。而当 \(k\) 取到 \(60\) 时,这个概率将会上升至 \(P'\approx 0.9999\)。实际上,假如一年中有 \(n\) 天,只需要 \(\sqrt{n}\) 级别的人数就可以满足要求。

上述数学模型与我们的实际经验严重不符,因此被称作生日悖论。

这个东西给了我们什么启发?考虑生日悖论的实质,实际上就是利用了 “组合随机采样” 的方法,满足答案的组合比单个个体要多,以此来提高正确率。那么怎么将这种思想运用到上面的分解因数中呢?

2.3 算法实现

2.3.1 随机算法的优化

我们利用 “组合随机采样” 的思想,考虑怎样进行组合。显然 \(n\) 与某个数的最大公约数一定是 \(n\) 的因数,也就是 \(\gcd(k,n)\mid n\)。那么我们只要选出一些 \(k\),使得 \(1<\gcd(k,n)<n\),那么就可以求出一个 \(n\) 的非平凡因子了。满足条件的 \(k\) 不少,\(n\) 的任意一个质因子的倍数都是可行的。

那么 Pollard-Rho 算法使用了一个随机函数生成一个序列 \(\{x_i\}\)。我们设 \(f(x)=(x^2+c)\bmod n\),其中 \(c\) 是随机的一个常数。接下来随机选取一个 \(x_1\),然后令 \(x_i=f(x_{i-1})\)

如果你注意力较高,会发现一件事:由于 \(f\) 函数的取值只有 \(n\) 种,所以总会有一个时刻 \(x\) 数列的生成进入循环,得到相同的结果,如下图所示:

(发现它长得很像一个字母 \(\rho\)​,所以这个算法的名字叫做 Pollard-Rho)

根据生日悖论可知,这个数列中不同值的数量约为 \(O(\sqrt n)\) 个。这个时候我们设 \(m\)\(n\) 的最小质因子,再生成一个数列 \(\{y_i\}\) 满足 \(y_i=x_i\bmod m\)。此时再根据生日悖论可知不同元素的个数约为 \(O(\sqrt m)\le O(n^{\frac 14})\)。于是我们可以在期望 \(O(n^{\frac 14})\) 的时间复杂度之内找出两个值 \(x_i,x_j\),使得 \(x_i \ne x_j\)\(y_i=y_j\),这就说明 \(n \nmid |x_i-x_j|\)\(m\mid |y_i-y_j|\),即 \(\gcd(n,|x_i-x_j|)>1\)

下面介绍 Pollard-Rho 算法的两种实现方式。

2.3.2 具体流程

2.3.2.1 Floyd 判环

考虑经典的小学奥数:两个人在同一个圆上跑,一快一慢,经过一段时间后两者必会相遇。那我们在 \(x\) 数列上模拟这个过程。设两个指针 \(a,b\),一个以一倍速跑,一个以两倍速跑(即一个是 \(a\leftarrow f(a)\),一个是 \(b\leftarrow f(f(b))\)),这样两者总会相遇,相遇时就出现了环。

然后我们每一次去记录两个 \(a,b\) 对应的 \(\gcd(|a-b|,n)\),看是否符合条件。当出现环的时候,后面就不必再遍历了,此时如果没有找到因子就只能重新调整上述随机函数的参数然后重新分解。

代码如下:

int f(int x, int c, int n) {return (mul(x, x, n) + c) % n;
}int Pollard_Rho(int n, int c) {int a, b;int x = rnd() % (n - 1) + 1;a = f(x, c, n), b = f(f(x, c, n), c, n);while(a != b) {int d = __gcd(abs(a - b), n);if(d > 1 && d < n) return d;a = f(a, c, n);b = f(f(b, c, n), c, n);}return n;
}int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);//...int p = n;while(p >= n) {p = Pollard_Rho(n, rnd() % (n - 1) + 1);}//...return 0;
}

(实测证明,上述 Floyd 判环代码正确率较下面做法要低)

2.3.2.2 倍增优化

上述过程我们是在同时跑 \(a,b\)。在倍增优化中,我们每一次固定住 \(b\),让 \(a\) 去跑一个固定的长度 \(k\)。每一次求出 \(\gcd(|a-b|,n)\) 并判断。当 \(a\) 跑完之后将 \(b\) 改为 \(a\),同时 \(k\leftarrow 2\times k\),然后不断重复上述过程直到 \(a,b\) 相遇。

代码如下:

int f(int x, int c, int n) {return (mul(x, x, n) + c) % n;
}int Pollard_Rho(int n, int c) {int a, b, i = 1, k = 2;a = rnd() % (n - 1) + 1, b = a;while(1) {a = f(a, c, n);int d = __gcd(abs(a - b), n);if(d > 1 && d < n) return d;if(a == b) return n;if(++i == k) {k <<= 1;b = a;}}return n;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/786291.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深痛教训 2024.8.23

strlen 这种东西千万不要重复调用多次,不然会 T 飞。 单次复杂度是 \(O(len)\) 的,调用 \(n\) 次直接卡成 \(n^2\) 了。 死亡回放:评测机波动抽象,以上全是同一份代码,死因为 strlen。

ST 表

ST 表 ST 表,主要思想是空间换时间,用于解决可重复贡献问题和 RMQ 问题。 可重复贡献问题 指某个运算 \(op\),有 \(x\ op\ x\ =\ x\) 。例如 \(max(x,x)=x\ \ min(x,x)=x\ \ gcd(x,x)=x\)。 RMQ 问题 指在区间内的最大/最小值查询。 ST 表 ST 表基于倍增的思想,做到 \(O(n …

Note - kruskal 重构树

点权多叉重构树 Kruskal 重构树不仅适用于限制边权的题目,也可以处理限制点权的情况。 在某 多校冲刺 NOIP 联训测试 2021 和 CF1797F 出现了这种方法。Alex_wei的博客 进行了详细讲解。 \(Problem1.\) 「NOIP 多校联训 2021」超级加倍参考资料Alex_wei本文来自博客园,作者:…

[ARC177F] Two Airlines

DP 优化My Blogs [ARC177F] Two Airlines 有点魔怔的题。 一个基本的观察是如果当前某个人 \(A\) 拿着盒子走到了位置 \(i\),那位置小于 \(i\) 的人一定永远没用了。如果之后要用到前面的人 \(B\),就应当让 \(B\) 拿着盒子走到 \(i\) 而不是让 \(A\),这样 \(A\) 待在原来的位…

helm原理及实践

目录为什么用HelmHelm是什么简介设计目标架构核心概念如何使用客户端命令使用 为什么用Helm 它使Kubernetes应用程序的配置、部署和维护变得更加简单、可控和可重复。优势 描述模板化配置 Helm 将应用程序的配置参数化,并使用模板引擎将这些参数嵌入到配置文件中。这使得配置更…

SAP S4HANA 2023 FPS01 FAA虚拟机发布了

SAP S4HANA 2023 FPS01 FAA虚拟机发布了。 系统不再需要修改虚拟机日期了,提供最高长达三年的许可,业务财务做账都是真实的时间!该虚拟机版本优点: 新版的一键启动脚本,3分钟就能启动完成。内存加载 80GB 就可以启动所有服务。不需要修改虚拟机日期,完美支持业务操作和财…

k8s介绍-英文版

目录WhyWhatSynopsisDesign GoalsArchitectureimage1image2image3Main ComponetsCore Resoucessome core resoucesrelated cmdsHowImplementation MechanismData Flow Why We need a set of tools and technologies designed to efficiently deploy, manage, and orchestrate c…

线段树(2)——懒惰标记Lazy Tag(单运算)及例题

上一篇文章我们讲了线段树的最基本的操作。如果有一种操作叫做区间加法呢?这个时候显然可以依次单点修改,但是时间复杂度太高了。所以可以考虑优化,由于思考过程可能很长,此处直接引入懒惰标记。 懒惰标记就是在对一颗树的所有节点进行某种统一操作时,只对根节点做一个标记…

Python保存数据为xlsx格式

参考代码 运行下面的代码,首先要安装下面这两个库:pandas openpyxlimport pandas as pd processed_data = [{"日期":"20230809","品牌":"Apple"},{"日期":"20230422","品牌":"Huawei"}, ] …

[思考] Diffusion Model

时间线 以下是一些重要的里程碑,它们代表了基于Diffusion的图像生成方法的发展:时间&机构 名称 简述- VAE Variational AutoEncoder,变分自编码器用于图像生成2020.12 VQ-VAE Vector Quantized-Variational AutoEncoder,一种用于生成模型的量化技术2020.12 VQ-GAN Vect…

如何正确使用搜索引擎(屏蔽csdn)

浏览器星愿浏览器 我使用的是星愿浏览器,推荐使用,其中有个性化的设置和搜索引擎的优化辅助,搜索引擎我选择的有:百度 必应 谷歌 DuckDuckGo 检索过程中想要快速切换各种搜索引擎,星愿浏览器有提供辅助拓展插件这里主要推荐每氪净化,可以自动添加屏蔽,例如CSDN搜索后结果…

Pollard Rho 算法

Pollard Rho 算法 难评,看OI-WIKI吧。 引入 Pollard Rho 算法用于求快速找到一个正整数 \(n\) 的一个非平凡因数[1]。 生日悖论不考虑出生年份(假设每年都是365天),问:一个房间中至少多少人,才能使其中两个人生日相通的概率达到 \(50\%\)?解:假设一年有 \(n\) 天,房间…