我正在使用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万的差异,所以它应该意味着我删除了无法写成两个数字之和的数字。但是当我重新阅读代码时,它看起来很合理......
请从这绝望中保存
答案 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中的开销较小(在当前范围内运行而不是创建新范围)。