简体   繁体   English

你将如何实现除数函数?

[英]How would you implement a divisor function?

The divisor function is the sum of divisors of a natural number. 除数函数是自然数的除数之和。

Making a little research I found this to be a very good method if you want to find the divisor function of a given natural number N, so I tried to code it in Python: 进行一些研究我发现如果你想找到给定自然数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

It works pretty well,but I am sure that it can be improved(eg : I create a list with 100000 elements,but I am not using most of them). 它工作得很好,但我确信它可以改进(例如:我创建一个包含100000元素的列表,但我没有使用它们中的大部分)。

How would you improve/implement it? 你会如何改进/实施它?

PS This is prime_factors : PS这是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

When computing the sum of divisors, you need the factorization of n in the form p 1 k 1 p 2 k 2 ... — that is, you need the exponent of each prime in the factorization. 计算除数之和时,需要以p 1 k 1 p 2 k 2 ...的形式 n进行因式分解 - 也就是说,需要分解中每个素数的指数。 At the moment you are doing this by computing a flat list of prime factors, and then calling count to work out the exponent. 目前,您通过计算一个素数因子的平面列表,然后调用countcount指数。 This is a waste of time because you can easily generate the prime factorization in the format you need in the first place, like this: 这是浪费时间,因为您可以首先以您需要的格式轻松生成素数分解,如下所示:

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

Notes on the code above: 关于上面代码的注释:

  1. I've transformed this code so that it generates the factorization, instead of constructing a list (by repeated calls to append ) and returning it. 我已经转换了这个代码,以便它生成分解,而不是构建一个列表(通过重复调用append )并返回它。 In Python, this transformation is nearly always an improvement, because it allows you to consume elements one by one as they are generated, without having to store the whole sequence in memory. 在Python中,这种转换几乎总是一种改进,因为它允许您在生成元素时逐个使用它们,而不必将整个序列存储在内存中。

  2. This is the kind of function for which doctests work well. 这是doctests运行良好的功能。

Now computing the sum of divisors is really simple: there's no need to store the set of checked factors or to count the number of times each factor occurs. 现在计算除数之和非常简单:不需要存储检查因子集或计算每个因子出现的次数。 In fact you can do it in just one line: 事实上,你可以只用一行:

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)

You need to change two lines only: 您只需要更改两行:

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

why use dict or set - or count() - at all, when prime_factors() is guaranteed to return the factors in ascending order? 为什么要使用dictset -或count() - 在所有的时候prime_factors()保证返回的因素升序排列? You only ever deal with a previous factor. 你只处理过前一个因素。 Counting can just be a part of iteration: 计数只能是迭代的一部分:

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
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

Here is what I do in my Java number utilities (extensively used for Project Euler): 这是我在Java数字实用程序(广泛用于Project Euler)中所做的事情:

  • Generate the prime factorization with explicit exponents (see the answer by Gareth Rees). 使用显式指数生成素数因子分解(参见Gareth Rees的答案)。

  • Unfold the prime factorization in the various functions based on it. 基于它展开各种功能中的素数因子分解。 Ie, use the same algorithm as for prime factorization but directly compute the desire value instead of storing the factors and exponents. 即,使用与素数因子分解相同的算法,但直接计算期望值而不是存储因子和指数。

  • By default test only divisors two and odd numbers. 默认情况下,只测试除数为2和奇数的除数。 I have methods firstDivisor(n) and nextDivisor(n,d) for that. 我有方法firstDivisor(n)nextDivisor(n,d)

  • Optionally precompute a table of least divisors for all numbers below a bound. (可选)为绑定下的所有数字预先计算最小除数的表。 This is very useful if you need to factorize all or most numbers below the bound (improves speed by about sqrt(limit) ). 如果您需要将全部或大多数数字分解到界限之下(通过大约sqrt(limit)提高速度),这非常有用。 I hook the table into the firstDivisor(n) and nextDivisor(n,d) methods, so this doesn't change the factorization algorithms. 我将表挂钩到firstDivisor(n)nextDivisor(n,d)方法,因此这不会改变分解算法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM