Miller-rabin素性测试与pollard-rho快速质因数分解

本文介绍了Miller-Rabin素性测试算法及其在一定范围内的确定性应用,通过费马小定理及二次探测增强判断准确性,并讨论了针对Carmichael数的处理方式。同时,文章还探讨了Pollard Rho算法用于整数分解的方法,包括随机函数构造、差值求最大公约数等技巧。

~~~~

Miller-Rabin

可以在O(log n)的时间内检测一个数是否为质数。
理论上是一个概率算法,但在一定范围以内的数经过验证全部可以判出来。
所以OI范围内可以视作是一个确定算法。

费马小定理

若p是质数,且a<pa<p,则有
ap1=1(modp)ap−1=1(modp)

其逆定理不一定成立,但不符合逆定理的一定不是素数。
根据这个检测n的素性。

即:尝试若干个a(a与n不能有公约数(欧拉定理条件),即对于质数n而言a不能是n的倍数,对于合数而言没关系了)
检测an1modn1an−1modn是否为1

然而这还不够强,会存在判不出的情况
对于Carmichael数,第一个是561,任意与他互质的数判出来都是Yes.

二次探测

显然地,对于质数n与正整数a<=n
a2=1(modn)a2=1(modn),则有n|(a+1)(a1)n|(a+1)(a−1)
又因为n是质数,a<=na<=n,所以a=1或a=n-1.

与费马小相同,若不符合此条件则n一定不是质数。

both are ok

两个加起来,设mr(n,a)表示以a为底数,判定n的返回结果。
将n-1中所有约数2提出来。设去除所有2之后的的数是d.
从d开始二次探测即可。最后考虑一下费马小。

int mr(ll x,ll a) {
    a%=x;
    if (a==0) return 1; //注意a不能是x的倍数。
    ll d=x-1;
    while (!(d&1)) d>>=1;
    ll t=ksm(a,d,x);
    for (; d<x; d<<=1) {
        if (t==1) return 1; //后面所有t都是1,没必要继续判了。
        if (t!=x-1) {
            t=t*t%x;
            if (t==1) return 0; //若后一个是1,且当前不是x-1与1,那么合数。
        } else t=t*t%x;
    }
    return t==1;  //要是撑到最后就要看费马小的结论了。
}

以下数据来自matrix67
1. 如果被测数小于4 759 123 141,那么只需要测试三个底数2, 7和61就足够了
2. 如果你每次都用前7个素数(2, 3, 5, 7, 11, 13和17)进行测试,所有不超过341 550 071 728 320的数都是正确的。
3. 如果选用2, 3, 7, 61和24251作为底数,那么10^16内唯一的强伪素数为46 856 248 255 981。
当然最好需要rand若干个数。

由上可知,检测一个数n的时间是logn的。

pollar rho

据说时间复杂度是n0.25n0.25,反正比较玄学吧。

利用生日悖论的结论,作差撞因子会比直接枚举概率大(很多)(超级反直觉)。发现可以利用gcd求约数,用log的时间来做一个炒鸡优化。

大概做法

假设我要分解n,那么
首先每一次用miller rabin判一下n是否质数。

然后定义个随机函数f(x)=(x^2+c)%c

两个数a,b,a随机,b在a的基础上走一步(取一个f),他们的差是d,将d的绝对值与n求gcd,若不为1或n则找到了一个因子,分下去即可。

若为1或n,那么要对a,b进行更改,可以用一个随机函数f(x)=(x^2+c)%mo,c是rand()出来的一个常数。

如果能保证每一对a,b都是有效的,一直做下去很快就能找到因子。(生日悖论)
但会发现可能有循环节,这时候无论多少次都是是出不了解的。

用一下floyed的判圈思想,b每次走2步,a每次走1步。这样当a,b重合时(套1~2圈)就不需要继续判断了,重新找c(重构随机函数)。

为什么a,b初值要间隔一步: 若a,b一开始取相同值,则4是无论如何也判不出来的。 大概和他只有长度为2的圈有关吧。

bzoj4802,注意有longlong相乘。

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <ctime>
#define abs(a) ((a)>0?(a):-(a))
#include <algorithm>
using namespace std;
typedef long long ll;
typedef long double ld;
ll gcd(ll a,ll b) {return b==0?a:gcd(b,a%b);}

ll _(ll a,ll b,ll c) {
    if (a>=c)a%=c; if (b>=c)b%=c;
    ll d=(ld)a*b/c;
    ll r=a*b-d*c;
    return (r%c+c)%c;
}
ll n;
ll ksm(ll a,ll b,ll mo) {
    ll ret=1;
    for (ll i=1; i<=b; i*=2) {
        if (b&i) ret=_(ret,a,mo);
        a=_(a,a,mo);
    }
    return ret;
}
int mr(ll x,ll a) {
    a%=x;
    if (a==0) return 1;
    ll d=x-1;
    while (!(d&1)) d>>=1;
    ll t=ksm(a,d,x);
    for (; d<x; d<<=1) {
        if (t==1) return 1;
        if (t!=x-1) {
            t=_(t,t,x);
            if (t==1) return 0; 
        } else t=_(t,t,x);
    }
    return t==1;
}
int isprime(ll x) {
    return mr(x,2) && mr(x,7) && mr(x,61) && mr(x,rand());
}
ll fo[10000];
int cs=0;

void pr(ll x) {
    if (isprime(x)) {
        fo[++fo[0]]=x;
        return;
    }
    int flag=fo[0];
    while (flag==fo[0]) {
        ll c=rand()%x,a=rand()%x;
        ll b=(_(a,a,x)+c)%x;
        while (a!=b) {
            cs++;
            ll det=gcd(abs(a-b),x);
            if (det>1 && det<x) {
                pr(x/det);
                pr(det);
                return;
            }
            a=(_(a,a,x)+c)%x;
            b=(_(b,b,x)+c)%x;
            b=(_(b,b,x)+c)%x;
        }
    }
}
int main() {
    freopen("pol.in","r",stdin);
//  freopen("pol.out","w",stdout);
    srand(time(0));

    rand();
    ll nn=0;
    cin>>nn;
    for (ll n=nn; n<=nn; n++) {
        if (n==1) {
            cout<<"1"<<endl; return 0;
        }
        fo[0]=0;
        pr(n);
        sort(fo+1,fo+1+fo[0]);
        ll ans=1;
        for (int i=1,cs=0; i<=fo[0]+1; i++) {
            if ( i==fo[0]+1 || i!=1 && fo[i-1] != fo[i]) {
                ll d = ksm(fo[i-1],cs,n); if (d==0) d=n;
                d-=ksm(fo[i-1],cs-1,n);
                ans*=d; 
                cs=1;
            } else cs++;
        }
        cout<<ans<<endl;
    }
}
对于大整数 `2019383291023828921092383` 的质因数分解,通常需要使用高效的算法,因为该数远远超出了普通试除法的计算效率范围。试除法虽然简单,但其时间复杂度较高,对于大数并不适用。在这种情况下,可以考虑使用 Pollard's Rho 算法、二次筛法(Quadratic Sieve)或更高级的数域筛法(Number Field Sieve)等[^4]。 下面提供一个基于 Pollard's Rho 算法的 Python 实现示例,用于对大整数进行质因数分解。此代码能够处理较大的整数,并尝试找出其因数: ```python import random import math def is_prime(n): if n < 2: return False if n < 4: return True if n % 2 == 0: return False a = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] s = 0 d = n - 1 while d % 2 == 0: d //= 2 s += 1 for x in a: if x >= n: continue x = pow(x, d, n) if x == 1 or x == n - 1: continue for _ in range(s - 1): x = pow(x, 2, n) if x == n - 1: break else: return False return True def pollards_rho(n): if n % 2 == 0: return 2 if n % 3 == 0: return 3 if n % 5 == 0: return 5 while True: c = random.randint(1, n-1) f = lambda x: (pow(x, 2, n) + c) % n x, y, d = 2, 2, 1 while d == 1: x = f(x) y = f(f(y)) d = math.gcd(abs(x - y), n) if d != n: return d def factor(n): factors = [] def _factor(n): if n == 1: return if is_prime(n): factors.append(n) return d = pollards_rho(n) _factor(d) _factor(n // d) _factor(n) return sorted(factors) # 示例:对大整数进行质因数分解 n = 2019383291023828921092383 result = factor(n) print(f"{n} 的质因数分解结果为: {result}") ``` ### 说明 1. **is_prime** 函数使用 Miller-Rabin 素性检验来判断一个数是否为质数。 2. **pollards_rho** 函数实现了 Pollard's Rho 算法,用于寻找大整数的一个因数。 3. **factor** 函数递归地调用 Pollard's Rho 算法,直到找到所有质因数为止。 4. **factor** 返回一个包含所有质因数的列表。 ### 复杂性性能 Pollard's Rho 算法的时间复杂度大约为 $ O(\sqrt{p}) $,其中 $ p $ 是目标数的最小质因数。这种方法比试除法更高效,尤其是在处理大整数时[^4]。 ### 注意事项 1. 由于大整数的质因数分解可能需要较长时间,建议在高性能计算环境中运行。 2. 对于非常大的整数,可以考虑使用更高级的算法,如数域筛法(Number Field Sieve)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值