【问题标题】:How would you implement a divisor function?你将如何实现除数函数?
【发布时间】:2015-01-23 00:44:24
【问题描述】:

divisor function 是自然数的除数之和。

做了一点研究,我发现this 是一个非常好的方法,如果你想找到给定自然数 N 的除数函数,所以我尝试用 Python 编写代码:

def divisor_function(n):
    "Returns the sum of divisors of n"
    checked = [False]*100000
    factors = prime_factors(n)
    sum_of_divisors = 1 # It's = 1 because it will be the result of a product
    for x in factors:
        if checked[x]:
            continue
        else:
            count = factors.count(x)
            tmp = (x**(count+1)-1)//(x-1)
            sum_of_divisors*=tmp
            checked[x]=True
    return sum_of_divisors

它工作得很好,但我确信它可以改进(例如:我创建了一个包含100000 元素的列表,但我没有使用其中的大部分)。

您将如何改进/实施它?

附:这是prime_factors

def prime_factors(n):
    "Returns all the prime factors of a positive integer"
    factors = []
    d = 2
    while (n > 1):
        while (n%d==0):
            factors.append(d)
            n /= d
        d = d + 1
        if (d*d>n):
            if (n>1): factors.append(int(n));
            break;
    return factors

【问题讨论】:

  • 如果您事先知道不会添加“大多数”元素,请使用字典或集合而不是列表。

标签: python math numbers


【解决方案1】:

在计算除​​数之和时,您需要以 p1kn 进行因式分解sub>1p2k2 ... - 也就是说,您需要分解中每个素数的指数。目前,您通过计算素因子的平面列表来执行此操作,然后调用count 来计算指数。这是浪费时间,因为您可以轻松地首先生成所需格式的素数分解,如下所示:

def factorization(n):
    """
    Generate the prime factorization of n in the form of pairs (p, k)
    where the prime p appears k times in the factorization.

    >>> list(factorization(1))
    []
    >>> list(factorization(24))
    [(2, 3), (3, 1)]
    >>> list(factorization(1001))
    [(7, 1), (11, 1), (13, 1)]
    """
    p = 1
    while p * p < n:
        p += 1
        k = 0
        while n % p == 0:
            k += 1
            n //= p
        if k:
            yield p, k
    if n != 1:
        yield n, 1

以上代码注释:

  1. 我已经转换了这段代码,使其生成分解,而不是构造一个列表(通过重复调用append)并返回它。在 Python 中,这种转换几乎总是一种改进,因为它允许您在生成元素时一个一个地使用它们,而不必将整个序列存储在内存中。

  2. doctests 可以很好地发挥这种功能。

现在计算除数的总和非常简单:无需存储检查的因子集或计算每个因子出现的次数。实际上,您只需一行即可完成:

from operator import mul

def sum_of_divisors(n):
    """
    Return the sum of divisors of n.

    >>> sum_of_divisors(1)
    1
    >>> sum_of_divisors(33550336) // 2
    33550336
    """
    return reduce(mul, ((p**(k+1)-1) // (p-1) for p, k in factorization(n)), 1)

【讨论】:

    【解决方案2】:

    您只需要更改两行:

    def divisor_function(n):
        "Returns the sum of divisors of n"
        checked = {}
        factors = prime_factors(n)
        sum_of_divisors = 1 # It's = 1 because it will be the result of a product
        for x in factors:
            if checked.get(x,False):
                continue
            else:
                count = factors.count(x)
                tmp = (x**(count+1)-1)//(x-1)
                sum_of_divisors*=tmp
                checked[x]=True
        return sum_of_divisors
    

    【讨论】:

    • set 足够时,没有必要使用dictchecked = set(), x in checked, checked.add(x).
    • 为什么要使用dictset - 或count() - 当prime_factors() 保证以升序返回因子时。你只需要处理以前的因素。计数可以只是迭代的一部分:cnt=0; prev=0; for x in factors: if x==prev: cnt+=1 else: (if prev: sum_of_divs*=(prev**(count+1)-1)//(prev-1)); prev=x; cnt=1; ... .
    【解决方案3】:

    为什么要使用dictset - 或count() - prime_factors() 保证按升序返回因子时?您只需要处理 previous 因素。计数可以只是迭代的一部分:

    def divisor_function(n):
        "Returns the sum of divisors of n"
        factors = prime_factors(n)
        sum_of_divisors = 1 
        count = 0; prev = 0;
        for x in factors:
            if x==prev:
                count += 1
            else:
                if prev: sum_of_divisors *= (prev**(count+1)-1)//(prev-1)
                count = 1; prev = x;
        if prev: sum_of_divisors *= (prev**(count+1)-1)//(prev-1)
        return sum_of_divisors
    

    【讨论】:

      【解决方案4】:
      def sum_divisors(n):
          assert n > 0
          if n == 1:
              return 0
          sum = 1
          if n % 2 == 0:              # if n is even there is a need to go over even numbers
              i = 2
              while i < sqrt (n):
                  if n % i == 0:
                      sum = sum + i + (n//i)  # if i|n then n/i is an integer so we want to add it as well
                  i = i + 1
              if type (sqrt (n)) == int:  # if sqrt(n)|n we would like to avoid adding it twice
                  sum = sum + sqrt (n)
          else:
              i = 3
              while i < sqrt (n):     # this loop will only go over odd numbers since 2 is not a factor
                  if n % i == 0:
                      sum = sum + i + (n//i)  # if i|n then n/i is an integer so we want to add it as well
                  i = i + 2
              if type (sqrt (n)) == int:  # if sqrt(n)|n we would like to avoid adding it twice
                  sum = sum + sqrt (n)
          return sum
      

      【讨论】:

      • 这应该返回 sqrt(n) 迭代的总和
      【解决方案5】:

      这是我在我的 Java 编号实用程序(广泛用于 Project Euler)中所做的:

      • 使用显式指数生成素数分解(请参阅 Gareth Rees 的答案)。

      • 基于它展开各种函数中的素数分解。即,使用与素数分解相同的算法,但直接计算期望值,而不是存储因子和指数。

      • 默认情况下,测试仅除以两个和奇数。我有 firstDivisor(n)nextDivisor(n,d) 的方法。

      • 可以选择为所有低于界限的数字预先计算一个最小除数表。如果您需要将所有或大多数数字分解为低于界限(将速度提高大约sqrt(limit)),这将非常有用。我将表格挂接到firstDivisor(n)nextDivisor(n,d) 方法中,所以这不会改变分解算法。

      【讨论】:

        猜你喜欢
        • 2018-05-07
        • 2011-08-08
        • 1970-01-01
        • 1970-01-01
        • 2019-12-17
        • 2017-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多