Python - 为什么这个素数因子分解函数会从中获得更好的性能?

时间:2016-05-03 22:18:34

标签: python primes

我写了这个素数因子化函数:

def prime_factorization(n):
    prime_factors = {}
    for i in _prime_candidates(n):
        if n % i == 0:
            prime_factors[i] = 0
            while n % i == 0:
                n /= i
                prime_factors[i] += 1
    if n != 1: prime_factors[int(n)] = 1
    return prime_factors

def _prime_candidates(n):
    yield 2
    for i in range(3, int(n**.5)+1, 2):
        yield i

我的机器需要大约0.387秒,n = 10 ^ 13。但是如果我复制for循环的内容并在运行实际的for循环之前运行它的数字2,我得到相同的正确结果,但是对于n = 10 ^ 13,运行时间约为0.003秒。您可以在下面看到以下代码:

def prime_factorization(n):
    prime_factors = {}
    if n % 2 == 0:
        prime_factors[2] = 0
    while n % 2 == 0:
        n /= 2
        prime_factors[2] += 1
    for i in _prime_candidates(n):
        if n % i == 0:
            prime_factors[i] = 0
            while n % i == 0:
                n /= i
                prime_factors[i] += 1
    if n != 1: prime_factors[int(n)] = 1
    return prime_factors

def _prime_candidates(n):
    yield 2
    for i in range(3, int(n**.5)+1, 2):
        yield i

为什么这会带来如此巨大的性能提升?

编辑:我使用的是Python 3.5,并且我使用clock()模块的time函数进行基准测试。

2 个答案:

答案 0 :(得分:4)

在您的初始版本中,_prime_candidates传递10 ^ 13,因此它会生成最多为其平方根的候选项。

在你的第二个版本中,_prime_candidates通过了5 ^ 13,因为2的所有因子都被分开了。它产生的候选人数要少得多。

通过将_prime_candidates逻辑折叠到prime_factorization并在找到因子时重新计算上限,您可以获得更好,更全面的改进:

def prime_factorization(n):
    prime_factors = {}

    factor_multiplicity = 0
    while n % 2 == 0:
        n //= 2
        factor_multiplicity += 1
    if factor_multiplicity:
        prime_factors[2] = factor_multiplicity

    factor_bound = n**.5
    candidate = 3

    while candidate <= factor_bound:
        factor_multiplicity = 0
        while n % i == 0:
            n //= i
            factor_multiplicity += 1
        if factor_multiplicity:
            prime_factors[candidate] = factor_multiplicity
            factor_bound = n**.5
        candidate += 2

    if n != 1:
        prime_factors[n] = 1
    return prime_factors

请注意,对于足够大的n,由于浮点精度的限制,n**.5的计算最终会生成错误的边界。您可以通过比较candidate * candidate <= n或使用decimal模块之类的东西来计算绑定到足够的精度来解决这个问题。

答案 1 :(得分:1)

原因是_prime_candidates函数内部。 在您的第一个示例中,它会生成所有数字3,5,...,3162277,并且您尝试将所有这些候选项划分为n

在您的第二个示例中,您首先大大减少了n,因此_prime_candidates生成了数字3,5,...,34939。它的数字要少得多。