【问题标题】:Finding all divisors of a number optimization查找数字优化的所有除数
【发布时间】:2012-09-14 09:43:17
【问题描述】:

我编写了以下函数,它找到给定自然数的所有除数并将它们作为列表返回:

def FindAllDivisors(x):
    divList = []
    y = 1
    while y <= math.sqrt(x):
        if x % y == 0:
            divList.append(y)
            divList.append(int(x / y))
        y += 1
    return divList

除了输入一个 18 位数字时它真的很慢之外,它工作得非常好。您对我如何加快速度有什么建议吗?

更新

我有以下方法来检查基于费马小定理的素数:

def CheckIfProbablyPrime(x):
    return (2 << x - 2) % x == 1

这种方法在检查单个数字时非常有效,但是我不确定是否应该使用它来编译所有素数到某个边界。

【问题讨论】:

  • 您是否使用CheckIfPrime 来查看是否可以跳过某个 x 的除法?你应该小心这个,因为你可能会得到误报:CheckIfPrime 过滤了大多数数字,但一些复合仍然会产生 True!
  • @BenRuijl 你能举个例子吗,非常感谢!
  • 由于Fermat pseudoprimes,您的CheckIfPrime 功能无法正常工作。例如,CheckIfPrime(341) 为 True,但 341 = 11*31。如果CheckIfPrime 为 False,那么这个数肯定是合数,但反之则不成立。 [啊,对不起,错过了@BenRuijl 早先的相同效果的评论。不过,我会留下这个例子。] 但是,如果你只将它用作“CheckIfProbablyPrime”,它仍然很有用。
  • @DSM 非常感谢,我没有考虑到它不是双向的(费马定理)。我将其更改为 CheckIfProbablyPrime。我会采用你的方法。因此,为了使其具有确定性,我还应该检查所有先前产生的素数。
  • 继续我的最后一句话:如果你使用这个“CheckIfProbablyPrime”函数来列出候选除数,那么其中有几个伪素数可能不是问题。如果您以递增顺序测试可除性并删除您找到的所有因数,则通过构造任何除以原始数字的伪素数都将已删除其因数,因此不会除以余数。

标签: python math mathematical-optimization


【解决方案1】:

您可以通过计算质因数分解找到一个数的所有除数。每个除数必须是分解中素数的组合。

如果你有一个素数列表,这是一种简单的分解方法:

def factorize(n, primes):
    factors = []
    for p in primes:
        if p*p > n: break
        i = 0
        while n % p == 0:
            n //= p
            i+=1
        if i > 0:
            factors.append((p, i));
    if n > 1: factors.append((n, 1))

    return factors

这叫做审判分工。有更有效的方法可以做到这一点。有关概述,请参阅 here

计算除数现在非常简单:

def divisors(factors):
    div = [1]
    for (p, r) in factors:
        div = [d * p**e for d in div for e in range(r + 1)]
    return div

计算所有除数的效率取决于找到素数的算法(小概述here)和分解算法。对于非常大的数字,后者总是很慢,对此您无能为力。

【讨论】:

  • 我的第一个想法也是关于素数除数,但我不确定它们的搜索是否比直接将 N 除以所有数字更快 - 我们仍然会筛选直到 sqrt( N),然后为每个素数组合花费 O(1)(我们仍然需要遍历它)。
  • 我对我的问题进行了小修改。因此,如果我使用我当前的素性测试,这是否足够有效,或者我应该改变我的测试吗?
  • @Bob,可以预先计算素数列表,即使必须检查多个数字,也只需执行一次。因式分解的优点是只检查素数。对于一个数字,你是对的,这并不重要。
  • 这是一个很好的解决方案。我在我的问题中使用它。然而,该算法的一个错误是它会陷入无限循环。修复方法是在 divisors 方法中创建 div 列表的副本并在其上运行内部嵌套循环。 P.S - 我在 Java 而不是 Python 中运行了这个问题,我的经验就是基于此。
  • @Pawan,这可能是因为您在 div 的 for 循环中更新了 div 列表本身,然后您得到了一个无限循环。如果您使用列表推导式,则 div 仅在创建整个新 div 列表后更新。
【解决方案2】:

我建议将math.sqrt(x) 的结果存储在一个单独的变量中,然后对照它检查y。否则while的每一步都会重新计算,math.sqrt绝对不是轻量级的操作。

【讨论】:

  • 还可以尝试将结果附加到字符串而不是列表。
  • 这是代码优化,很好,但您将拥有相同的“大 O”。 Ben Ruijl 解决方案确实降低了算法的复杂性,从而减少了大数字所花费的时间。
【解决方案3】:

我会做一个素因子分解,然后根据该结果计算所有除数。

【讨论】:

    【解决方案4】:

    我不知道是否会对性能造成很大影响,但我很确定强制转换为 int 是不必要的。至少在 Python 2.7 中,int x / int y 返回一个 int。

    【讨论】:

    • 在 Python 3 中,它没有。在那里你可以使用//进行楼层划分。
    猜你喜欢
    • 1970-01-01
    • 2020-09-13
    • 2019-05-16
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    • 2018-06-04
    • 2017-06-24
    • 1970-01-01
    相关资源
    最近更新 更多