初等数学瞎扯Ⅱ:辅助工具

1|00. 前置知识

2|01. 乘方运算

2|11-0. 问题简述

bm(modp)

2|21-1. 普通快速幂

快速求 ab(modp)

朴素算法是 O(b) 的,考虑优化这一过程。

b 在二进制意义下的第 i 位值为 wi,则答案为 2iba2iwi。由于 wi={0,1},求出 a2i 即可,复杂度 O(logb)

typedef long long ll;
ll qp(ll a,ll b,ll p){
ll res=0;
for(;m;m>>=1){if(m&1)res=res*a%p;a=a*a%p;}
return res;
}

2|31-2. 光速幂

适用于 a 不变,b 较小,询问较多的情况。

我们令 b=xm+q,其中 q<x,则 ab=axm×aq,预处理 axmaq 即可 O(1) 回答。

注意到预处理部分复杂度是 O(bx+x) 的,由欧拉定理可知,bφ(p),由于 p 一般是质数,这里令 φ(p)p 同阶,根号平衡取 x=p 即可。

typedef long long ll;
ll pw[P][2];
void init(ll a,ll p){
ll f=sqrt(p);
for(int i=pw[0][0]=1;i<=f;i++)pw[i][0]=pw[i-1][0]*a%p;
for(int i=pw[0][1]=1;i<=p/f;i++)pw[i][1]=pw[i-1][1]*pw[f][0]%p;
}
ll qp(ll a,ll b,ll p){
ll f=sqrt(p)
return pw[b/f][1]*pw[b%f][0]%p;
}

3|02. 素性检验与质因数分解

3|12-0. 问题简述

判定一个数 n 是不是素数,若不是,对其分解质因数。

3|22-1. 素性检验

1|02-1-1. 试除法

即枚举 2n1 的所有数字 i,判断是否有 n0(modi) 即可,复杂度 O(n)

1|02-1-2. 优化的试除法

上面的做法看起来非常呆,我们考虑优化这一过程。

首先给出定理

n 为合数,则其最小素因子 p 一定不大于 n

证明:反证,若其最小素因子 p 大于 n,令 d=np,则 d<n,且 d 的最小素因子 p 一定满足 pd<n<p,且为 n 的一个不为 p 的素因子,与原假设矛盾,故其小于 n

也可以考虑另一种证明,若最小素因子 p 也是最小因子,则 np 一定大于 p,即要在大于 n 的数中找到两个数使其乘积等于 n,显然不成立,而第一个命题也是显然的,若 p 不是最小因子,设其有更小的因子 d,则 d 的最小素因子一定小于 p,与 p 为最小素因子矛盾。

那我们直接在 n 范围内枚举即可,复杂度 O(n)

1|02-1-3. Miller-Rabin 算法

1|02-1-3-1. Miller-Rabin 算法思想

上述的算法效率太低,我们考察一个更高效的算法。

首先用到费马小定理,可以参见初等数学瞎扯Ⅰ:同余相关1-1。

p 为质数,ap,则 ap11(modp)

我们写出其逆反命题

ap,ap11(modp),则 p 不是质数。

那么我们可以取一组 a,检查 ap1p 的值即可。这样大概率是对的。

但是这并不等价于若 ap11(modp),则 p 是质数。对于一些合数,有部分数字的 p1 次方模 p1,比如 23401(mod341)

然后可以考虑多随机几组 a,这样能排除大量的上述情况。

但是这并不等价于对于若干个 aap11(modp),则 p 是质数。

对于一类卡迈尔数,它满足对于所有 apap1(modp),比如 561

所以单上费马小定理是肯定不行的,接下来需要二次探测定理。

p 为质数,则 x21(modp) 有且仅有解 ±1

同理写出逆反命题

x21(modp) 的解不只有 ±1 ,则 p 不为质数。

具体证明在二次剩余里提到,关于二次剩余的知识可以参见初等数学瞎扯Ⅰ:同余相关二次剩余。

我们不妨令 p1=r×2d,则我们根据二次探测定理,当 ap11(modp) 时,若 p1 是 2 的倍数,则 ap121(modp) 的解必须是 ±1。以此类推,并重复 d 轮即可。

将这两种做法结合起来后,我们可以发现此时对合数误判的概率相当低,同时我们可以随机多组数据,以保证正确性。

Miller-Rabin 算法的复杂度与随机次数息息相关,若我们把单次乘法的复杂度记为 O(1),则 Miller-Rabin 算法的复杂度为 O(klog2n),其中 k 是随机轮数。

但对于 OI 而言,Miller-Rabin 算法是可被视作确定性算法的,具体原因是我们要判断的数一般值域在 [0,264) 次方内,而对于这个范围内的数字,已经被证明了可以用一组固定的 a 完成全部检测,具体可以参见wangrx的博客:论 Miller-Rabin 算法的确定性化,这里给出结论。

对于 232 内的数字检测,使用 2,7,61 即可。

对于 264 内的数字检测,使用 2,325,9375,28178,450775,9780504,1795265022 即可,但这种抽象东西我们一般不会去背,所以使用前 10 个质数即可完成检测。

关于更详细的信息,可以参见 OEIS

朴素实现的复杂度是 O(kdlogn) 的,视 dlogn 同阶,复杂度为 O(klog2n)

注意到其实仍有优化空间,复杂度瓶颈在二次探测。我们考虑把二次探测的过程翻过来,把 d×2rd 变为 dd×2r,我们只需要算出 ad,然后平方 r 次即可。复杂度 O(klogn)。当然你也可以搞光速幂,单次快速幂的复杂度降低为常数,但是这不是复杂度瓶颈,故略去。

注意 p 无法通过以 p 为底的素性检验,所以你需要特判底数。

1|02-1-3-2. Miller-Rabin 代码实现

ll prime_list[10]={2,3,5,11,17,19,23,37,41,43};
ll qp(ll b,ll m,ll p){
ll res=1;
for(;m;m>>=1,b=b*b%p)if(m&1)res=res*b%p;
return res;
}
bool miller_rabin(ll n,ll a){
ll d=n-1,r=0;while(!(d&1))d>>=1,r++;
ll x=qp(a,d,n);if(x==1)return 1;
for(int i=0;i<r;i++){if(x==n-1)return 1;x=x*x%n;}
return 0;
}
bool prime(ll n){
if(n<2)return 0;
for(int _=0;_<10;_++){
if(n==prime_list[_])return 1;
if(!miller_rabin(n,prime_list[_]))return 0;
}return 1;
}

__EOF__

本文作者EXODUS
本文链接https://www.cnblogs.com/-Complex-/p/17352441.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   -Comρℓex-  阅读(89)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示