4.3 Pollard‘s rho algorithm

理论基础

  阅读这篇文章,需要有双指针查环的算法基础。可以看我写的两篇博文Floyd链表查环算法Brent链表查环算法。为了辅助理解,还可以先读读我写过的线性同余伪随机数算法
  Pollard ρ \rho ρ算法是使用同余算法产生一个 [ 0 , n ) [0,n) [0,n)之间的伪随机数,这里的n是我们要进行分解的数字,不过不是这个算法线性同余,可以说是二次同余,因为用的函数是 f ( x ) = ( x 2 + c )    m o d    n f(x)=(x^2+c)\;mod \; n f(x)=(x2+c)modn
  根据生日悖论birthday paradox,这个随机数序列组成的链表一定会成环。我举个例子, c = 1 , x 1 = 3 , p = 72 c=1,x_1=3,p=72 c=1,x1=3,p=72。会生成如下序列:
在这里插入图片描述
  这个图形就长得像字母 ρ \rho ρ,所以叫 ρ \rho ρ算法。这个环的检测使用Brent或Floyd链表查环算法就行了。但是对于更大的数进行质因数分解,这个环会非常大,比如这个数2206637,如果对2206637取模,形成的环一个屏幕是装不下的。所以对这个序列中的值还可以继续对质因子取模。因为 6961 = 317 × 6961 6961 = 317 \times 6961 6961=317×6961,我们以 c = 1 , x 1 = 2 , p = 317 , n = 2206637 c=1,x_1=2,p=317,n=2206637 c=1,x1=2,p=317,n=2206637为例子,画一下对原序列再进行一次取模运算的效,这里我选择对小的质因子317进行取模:
在这里插入图片描述

  在上图中冒号前的是原值,冒号后的是对p再次取模后的值。169那个节点有个括号,表示取模前两个值是不同的,但是取模后都是169。那存在环意味着什么呢?这和质因数分解有什么关系呢?假设相遇的这个点和相遇前的一个点,在序列里的顺序(索引)分别为 s , t s,t s,t,以1开始。比如上图, s = 6 , t = 12 s=6,t=12 s=6,t=12。再设序列为 { x    m o d    p } \{x \;mod\; p\} {xmodp},注意这里的x是取模前的值, x s x_s xs x t x_t xt是对 p p p取模同余的,所以有:
x t ≡ x s    m o d    p ∣ x t − x s ∣ = k p , k ∈ Z x_t \equiv x_s \;mod\;p\\ |x_t-x_s|=kp,k\in\mathbb{Z} xtxsmodpxtxs=kp,kZ
  用上面的例子代进去更容易理解,上例中 x 6 = 1166412 , x 12 = 1264682 x_6=1166412,x_{12}=1264682 x6=1166412,x12=1264682(用前一项计算的结果),有:
∣ x 6 − x 12 ∣ = 98270 = 310 ∗ 317 = 310 p |x_6-x_{12}|=98270= 310*317=310p x6x12=98270=310317=310p
  因为p是我们需要求出的一个质因数,所以有:
n = p q n=pq n=pq
  如果形成了环,那么意味着, ∣ x t − x s ∣ = k p |x_t-x_s|=kp xtxs=kp,而 k p kp kp n = p q n=pq n=pq的最大公约数就是p的倍数。到这一步,意味着我们检验成环的条件变了,变成了求和n的最大公约数了。如果最大公约数不是1,那么这个数字就是n的一个因子。那我们就找到了一个,然后n再除于这个最大公约数,这样递归下去就完成了质因数分解。用公式总结就是:
x t ≡ x s    m o d    p ∣ x t − x s ∣ = k p , k ∈ Z g c d ( ∣ x t − x s ∣ , n ) = p x_t \equiv x_s \;mod\;p\\ |x_t-x_s|=kp,k\in\mathbb{Z}\\ gcd(|x_t-x_s|,n)=p xtxsmodpxtxs=kp,kZgcd(xtxs,n)=p
  然后我们再使用查环算法进行查环,代码就出来了。

通用代码

def gcd(m, n):
    if m < n:
        m, n = n, m
    while m % n != 0:
        m, n = n, m % n
    return n


def f(x, c, n):
    return (x * x + c) % n


def factorization(n, fac_fun):
    factors = []
    stack = [n]
    while len(stack) > 0:
        x = stack.pop()
        if x == 2:
            factors.insert(0, x)
            continue
        p, q = fac_fun(x) if x & 1 == 1 else (2, x // 2)
        if p == 1:
            factors.insert(0, q)
        else:
            stack.append(p)
            stack.append(q)
    return factors

Floyd查环代码实现

def floyd(n):
    c = 1
    x = 2
    slow = x
    fast = x
    while True:
        slow = f(slow, c, n)
        fast = f(f(fast, c, n), c, n)

        diff = abs(fast - slow)
        if diff != 0:
            g = gcd(diff, n)
            if g > 1:
                return g, n // g
        else:
            return 1, n

if __name__ == '__main__':
    n = 2206637
    print(factorization(n, rho))

  测试结果如下:

[317, 6961]

  注意代码中的细节,如果是对质数进行Pollard ρ \rho ρ算法分解,相差会得到0。

Brent查环代码实现

def brent(n):
    c = 1
    x = 2
    slow = x
    fast = f(x, c, n)
    power = 1
    steps = 1
    while True:

        diff = abs(fast - slow)
        if diff != 0:
            g = gcd(diff, n)
            if g > 1:
                return g, n // g
        else:
            return 1, n

        if steps == power:
            power <<= 1
            steps = 0
            fast = slow

        slow = f(slow, x, n)
        steps += 1


if __name__ == '__main__':
    n = 2206637
    print(factorization(n, brent))

  测试结果如下:

[317, 6961]
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒过来摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值