当前位置:   article > 正文

Python 大数的质因数分解_python因数分解

python因数分解

肝了一天总算把大数质因数分解搞定了,这篇文章主要涉及了 Pollard rho 算法和试除法

我用了若干个质数的平方来对比这两个算法的性能,发现:

  • 7e5 以上的数用 Pollard rho 算法更快,分解多大的数都不是问题
  • 7e5 以下的数用试除法更快

最终的质因数分解是由这两个算法构成的,主函数的思路是:

  • 当 n > 7e5 时,使用 Miller Rabin 算法判断 n 是不是质数,如果不是:
  1. 使用 Pollard rho 算法求解任意因数 a,并求出 a 对 n 的整除次数(n 更新为整除后的值)
  2. 递归分解出 a 的质因数;如果 n > 1,则递归分解出当前 n 的质因数
  • 当 n <= 7e5 时,使用试除法进行质因数分解

至此涉及到的算法有:试除法,Miller Rabin 素性测试,Pollard rho 因数分解

Miller Rabin 素性测试

对于素数 p 和随机数 a,如果两者互素,根据费马小定理有:

a^{p-1} - 1\equiv 0 \mod p

a 通常取预设的值,如:[2, 325, 9375, 28178, 450775, 9780504, 1795265022]

这时,如果 p 满足上式则很有可能是素数,否则一定是合数

因为素数都是奇数,所以 p - 1 一定是偶数,所以有:

p-1=2^r m, a^{p-1} - 1= a^{2^r m} - 1

a^{p-1} - 1 = (a^m-1) \prod_{i=0}^{r-1}(a^{2^i m}+1) \equiv 0 \mod p

a^{p-1} - 1 可被表示成若干个因子的累乘,如果其中有因子可以被 p 整除,则 a^{p-1} - 1\equiv 0 \mod p 成立

即当满足以下任意式子时,p 通过 a 的素性测试:

a^m \equiv 1 \mod p

a^m \equiv p-1 \mod p

a^{2m} \equiv p-1 \mod p

a^{4m} \equiv p-1 \mod p

......

a^{2^{r-1}m} \equiv p-1 \mod p

为了提高准确率,通常取不同的 a,进行 5 ~ 10 次测试

  1. def miller_rabin(p):
  2. ''' 素性测试'''
  3. # 特判 4
  4. if p <= 4: return p in (2, 3)
  5. # 对 p-1 进行分解
  6. pow_2, tmp = 0, p - 1
  7. while tmp % 2 == 0:
  8. tmp //= 2
  9. pow_2 += 1
  10. # 进行多次素性测试
  11. for a in (2, 3, 5, 7, 11):
  12. basic = pow(a, tmp, p)
  13. # a^m 是 p 的倍数或者满足条件
  14. if basic in (0, 1, p - 1): continue
  15. # 进行 r-1 次平方
  16. for _ in range(1, pow_2):
  17. basic = basic ** 2 % p
  18. # 怎样平方都是 1
  19. if basic == 1: return False
  20. # 通过 a 的素性测试
  21. if basic == p - 1: break
  22. # 未通过 a 的素性测试
  23. if basic != p - 1: return False
  24. # 通过所有 a 的素性测试
  25. return True

Pollard rho 因数分解

我比较菜,就不讲解原理了,具体见这篇文章:Link ~

  1. def pollard_rho(n):
  2. ''' 求因数: 7e5 以上'''
  3. # 更新函数
  4. bias = random.randint(3, n - 1)
  5. update = lambda i: (i ** 2 + bias) % n
  6. # 初始值
  7. x = random.randint(0, n - 1)
  8. y = update(x)
  9. # 查找序列环
  10. while x != y:
  11. factor = math.gcd(abs(x - y), n)
  12. # gcd(|x - y|, n) 不为 1 时, 即为答案
  13. if factor != 1: return factor
  14. x = update(x)
  15. y = update(update(y))
  16. return n

质因数分解

使用继承 collections.Counter 的类,主要是为了记录因数及其幂次,这个类主要有几个函数:

  • count:试除并求出幂次
  • main:主函数
> 7e5Miller Rabin 判素 + Pollard rho 求因数,递归分解因数、剩余部分
≤ 7e5试除法分解
  • try_div:试除法分解质因数

这个类可以当作函数来用,prime_factor(n) 的返回值就是一个字典

  1. class prime_factor(Counter):
  2. """ 质因数分解
  3. require: miller_rabin, pollard_rho"""
  4. def __init__(self, n):
  5. super().__init__()
  6. self.main(n, gain=1)
  7. def count(self, n, fac):
  8. # 试除并记录幂次
  9. cnt = 1
  10. n //= fac
  11. while n % fac == 0:
  12. cnt += 1
  13. n //= fac
  14. return n, cnt
  15. def main(self, n, gain):
  16. # 试除法求解
  17. if n < 7e5: return self.try_div(n, gain=gain)
  18. # 米勒罗宾判素
  19. if miller_rabin(n): return self.update({n: gain})
  20. # pollard rho 求解因数
  21. fac = pollard_rho(n)
  22. n, cnt = self.count(n, fac)
  23. # 递归求解因数的因数
  24. self.main(fac, gain=cnt * gain)
  25. # 递归求解剩余部分
  26. if n > 1: self.main(n, gain=gain)
  27. def try_div(self, n, gain=1):
  28. """ 试除法分解"""
  29. i, bound = 2, math.isqrt(n)
  30. while i <= bound:
  31. if n % i == 0:
  32. # 计数 + 整除
  33. n, cnt = self.count(n, i)
  34. # 记录幂次, 更新边界
  35. self.update({i: cnt * gain})
  36. bound = math.isqrt(n)
  37. i += 1
  38. if n > 1: self.update({n: gain})
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/669364
推荐阅读
相关标签
  

闽ICP备14008679号