我无法找到我做错的地方:(

时间:2017-05-01 12:35:49

标签: python

我正在使用python处理项目euler问题23。对于这个问题,我必须找到任何数字的总和< 28124,这两个数字的总和不能得到。数字丰富的数字小于其自身的适当除数之和。

我的apporach是:https://gist.github.com/anonymous/373f23098aeb5fea3b12fdc45142e8f7

from math import sqrt

def dSum(n): #find sum of proper divisors
    lst = set([])
    if n%2 == 0:
        step = 1
    else:
        step = 2
    for i in range(1, int(sqrt(n))+1, step):
        if n % i == 0:
            lst.add(i)
            lst.add(int(n/i))
    llst = list(lst)
    lst.remove(n)
    sum = 0
    for j in lst:
        sum += j
    return sum

#any numbers greater than 28123 can be written as the sum of two abundant numbers.
#thus, only have to find abundant numbers up to 28124 / 2  = 14062

abnum = [] #list of abundant numbers
sum = 0 
can = set([])

for i in range(1,14062):
    if i < dSum(i):
        abnum.append(i)

for i in abnum:
    for j in abnum:
        can.add(i + j)

print (abnum)
print (can)

cannot = set(range(1,28124))
cannot = cannot - can
cannot = list(cannot)
cannot.sort ()

result = 0

print (cannot)

for i in cannot:
    result += i

print (result)

给了我31531501的回答,这是错误的。

我用Google搜索答案,答案应该是4179871.

这些答案之间存在100万的差异,所以它应该意味着我删除了无法写成两个数字之和的数字。但是当我重新阅读代码时,它看起来很合理......

请从这绝望中保存

2 个答案:

答案 0 :(得分:0)

只是为了一些经验,你真的应该看一下理解和利用内置(而不是隐藏它们):

你在dSum()之外的循环(也可以简化)看起来像:

import itertools as it

abnum = [i for i in range(1,28124) if i < dSum(i)]
can = {i+j for i, j in it.product(abnum, repeat=2)}

cannot = set(range(1,28124)) - can
print(sum(cannot)) # 4179871

答案 1 :(得分:0)

有几种方法可以改进您的代码。

首先,这是dSum的更紧凑版本,它与您的代码非常接近。运算符通常比函数调用更快,因此我使用** .5而不是调用math.sqrt。我使用条件表达式而不是if...else块来计算步长。我使用内置的sum函数而不是for循环来累加除数;另外,我使用整数减法从总数中删除n,因为这比调用set.remove方法更有效。

def dSum(n):
    lst = set()
    for i in range(1, int(n ** .5) + 1, 2 if n % 2 else 1):
        if n % i == 0:
            lst.add(i)
            lst.add(n // i)
    return sum(lst) - n

但是,我们并不需要在这里使用一套。我们可以在找到它们时添加除数对,如果我们注意不要两次添加任何除数。

def dSum(n):
    total = 0
    for i in range(1, int(n ** .5) + 1, 2 if n % 2 else 1):
        if n % i == 0:
            j = n // i
            if i < j:
                total += i + j
            else:
                if i == j:
                    total += i
                break
    return total - n

这稍微快一点,并且使用较少的RAM,代价是增加了代码复杂性。但是,解决这个问题的方法更为有效。

不是单独找到每个数字的除数(因此除数和),而是使用筛选方法更好地找到所需范围内所有数字的除数。这是一个简单的例子。

num = 28124

# Build a table of divisor sums.
table = [1] * num
for i in range(2, num):
    for j in range(2 * i, num, i):
        table[j] += i

# Collect abundant numbers
abnum = [i for i in range(2, num) if i < table[i]] 
print(len(abnum), abnum[0], abnum[-1])

<强>输出

6965 12 28122

如果我们需要找到一个非常大的num的除数和,一个好的方法是找到每个数的主要幂因子,因为这是计算除数之和的有效方法。主要功率因子分解。但是,对于数字这个小的节省时间并不能保证额外的代码复杂性。 (但是如果你好奇的话,我可以添加一些主要的电力筛码;为了找到所有数字的除数和&lt; 28124,主要的电力筛选技术的速度大约是上述代码的两倍)。

AChampion的答案显示了一种非常紧凑的方法,可以找到不能写成两个数字之和的数字之和。但是,它有点慢,主要是因为它遍历abnum中所有丰富数字对。这是一种更快捷的方式。

def main():
    num = 28124

    # Build a table of divisor sums. table[0] should be 0, but we ignore it.
    table = [1] * num
    for i in range(2, num):
        for j in range(2 * i, num, i):
            table[j] += i

    # Collect abundant numbers
    abnum = [i for i in range(2, num) if i < table[i]] 
    del table

    # Create a set for fast searching
    abset = set(abnum)
    print(len(abset), abnum[0], abnum[-1])

    total = 0
    for i in range(1, num):
        # Search for pairs of abundant numbers j <= d: j + d == i 
        for j in abnum:
            d = i - j
            if d < j:
                # No pairs were found
                total += i
                break
            if d in abset:
                break

    print(total)

if __name__ == "__main__":
    main()

<强>输出

6965 12 28122
4179871

这个代码在运行Python 3.6.0的旧32位单核2GHz机器上运行大约2.7秒。在Python 2上,它的速度提高了约10%;我认为这是因为列表推导在Python 2中的开销较小(在当前范围内运行而不是创建新范围)。