赞
踩
肝了一天总算把大数质因数分解搞定了,这篇文章主要涉及了 Pollard rho 算法和试除法
我用了若干个质数的平方来对比这两个算法的性能,发现:
最终的质因数分解是由这两个算法构成的,主函数的思路是:
至此涉及到的算法有:试除法,Miller Rabin 素性测试,Pollard rho 因数分解
对于素数 p 和随机数 a,如果两者互素,根据费马小定理有:
a 通常取预设的值,如:[2, 325, 9375, 28178, 450775, 9780504, 1795265022]
这时,如果 p 满足上式则很有可能是素数,否则一定是合数
因为素数都是奇数,所以 p - 1 一定是偶数,所以有:
可被表示成若干个因子的累乘,如果其中有因子可以被 p 整除,则 成立
即当满足以下任意式子时,p 通过 a 的素性测试:
......
为了提高准确率,通常取不同的 a,进行 5 ~ 10 次测试
- def miller_rabin(p):
- ''' 素性测试'''
- # 特判 4
- if p <= 4: return p in (2, 3)
- # 对 p-1 进行分解
- pow_2, tmp = 0, p - 1
- while tmp % 2 == 0:
- tmp //= 2
- pow_2 += 1
- # 进行多次素性测试
- for a in (2, 3, 5, 7, 11):
- basic = pow(a, tmp, p)
- # a^m 是 p 的倍数或者满足条件
- if basic in (0, 1, p - 1): continue
- # 进行 r-1 次平方
- for _ in range(1, pow_2):
- basic = basic ** 2 % p
- # 怎样平方都是 1
- if basic == 1: return False
- # 通过 a 的素性测试
- if basic == p - 1: break
- # 未通过 a 的素性测试
- if basic != p - 1: return False
- # 通过所有 a 的素性测试
- return True
我比较菜,就不讲解原理了,具体见这篇文章:Link ~
- def pollard_rho(n):
- ''' 求因数: 7e5 以上'''
- # 更新函数
- bias = random.randint(3, n - 1)
- update = lambda i: (i ** 2 + bias) % n
- # 初始值
- x = random.randint(0, n - 1)
- y = update(x)
- # 查找序列环
- while x != y:
- factor = math.gcd(abs(x - y), n)
- # gcd(|x - y|, n) 不为 1 时, 即为答案
- if factor != 1: return factor
- x = update(x)
- y = update(update(y))
- return n
使用继承 collections.Counter 的类,主要是为了记录因数及其幂次,这个类主要有几个函数:
> 7e5 | Miller Rabin 判素 + Pollard rho 求因数,递归分解因数、剩余部分 |
≤ 7e5 | 试除法分解 |
这个类可以当作函数来用,prime_factor(n) 的返回值就是一个字典
- class prime_factor(Counter):
- """ 质因数分解
- require: miller_rabin, pollard_rho"""
-
- def __init__(self, n):
- super().__init__()
- self.main(n, gain=1)
-
- def count(self, n, fac):
- # 试除并记录幂次
- cnt = 1
- n //= fac
- while n % fac == 0:
- cnt += 1
- n //= fac
- return n, cnt
-
- def main(self, n, gain):
- # 试除法求解
- if n < 7e5: return self.try_div(n, gain=gain)
- # 米勒罗宾判素
- if miller_rabin(n): return self.update({n: gain})
- # pollard rho 求解因数
- fac = pollard_rho(n)
- n, cnt = self.count(n, fac)
- # 递归求解因数的因数
- self.main(fac, gain=cnt * gain)
- # 递归求解剩余部分
- if n > 1: self.main(n, gain=gain)
-
- def try_div(self, n, gain=1):
- """ 试除法分解"""
- i, bound = 2, math.isqrt(n)
- while i <= bound:
- if n % i == 0:
- # 计数 + 整除
- n, cnt = self.count(n, i)
- # 记录幂次, 更新边界
- self.update({i: cnt * gain})
- bound = math.isqrt(n)
- i += 1
- if n > 1: self.update({n: gain})
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。