你的第一个筛质数的部分完全没问题。只有第二个找因子的部分需要改正。
在第二部分中,你应该除以质因数不止一次,而是多次,直到不能再被整除为止。这样可以确保考虑到所有质因数的幂次。
这个第二部分叫做用质数进行试除法。众所周知,只需要用小于Sqrt(N)
的除数试除即可。剩下的N的值自然也会是质数。
对你的算法来说,还有一个非常重要的速度改进方法,就是将筛子大小设为Sqrt(N)
,而不是N
。因为我们只需要小于Sqrt(N)
的质数。这是因为经过试除后,剩下的N
自动成为了质数。
我还发现了一个改进你筛选部分的方法,你应该用for i in range(x * x, ...
替换for i in range(x + x, ...
。因为你可以根据埃拉托斯特尼筛法的经典算法,从x * x
开始筛选,而不是从x + x
开始。这将进一步提高速度。从x + x
开始筛选也可以,但x * x
将以更好的性能给出正确的结果。
另一个明显的改进是当你想要分解多个数字时,你可以多次重复使用筛子并扩展它,这将使代码更快。在重复使用筛子时,最好将得到的质数存储在单独的列表中,这样在第二次试除阶段,你不需要遍历整个筛子,而只需要遍历质数,这将再次加速。我没有在下面的代码中实现这两个改进,但如果你想要,我可以做到,只需告诉我。
完整的修正最终版本如下:
在线试用!
def factorize(n):
sieve = [True] * int(n ** 0.5 + 2)
for x in range(2, int(len(sieve) ** 0.5 + 2)):
if not sieve[x]:
continue
for i in range(x * x, len(sieve), x):
sieve[i] = False
factors = []
for i in range(2, len(sieve)):
if i * i > n:
break
if not sieve[i]:
continue
while n % i == 0:
factors.append(i)
n //= i
if n > 1:
factors.append(n)
return factors
print(factorize(99020))
输入:
99020
输出:
[2, 2, 5, 4951]
如果您只需要分解几个数字,使用试除法分解而不进行中间筛选阶段可能会更快。这也使代码更简单、更短。
以下是在没有筛选阶段的情况下执行分解的完整代码。
在此代码中,我通过仅尝试奇数除数来进行一项明显的速度改进,这将使总算法速度加倍。当然,在此之前,您需要分解出所有2的幂次方,我也已经做到了。
在线尝试!
def factorize(n):
factors = []
while (n & 1) == 0:
factors.append(2)
n >>= 1
for d in range(3, 1 << 60, 2):
if d * d > n:
break
while n % d == 0:
factors.append(d)
n //= d
if n > 1:
factors.append(n)
return factors
print(factorize(99020))