我有Project Euler Problem 12的以下代码。但是,执行需要很长时间。有没有人有加快速度的建议?
n = input("Enter number: ")
def genfact(n):
t = []
for i in xrange(1, n+1):
if n%i == 0:
t.append(i)
return t
print "Numbers of divisors: ", len(genfact(n))
print
m = input("Enter the number of triangle numbers to check: ")
print
for i in xrange (2, m+2):
a = sum(xrange(i))
b = len(genfact(a))
if b > 500:
print a
对于n,我输入一个任意数字,例如6,只是为了检查它是否确实返回了因子数列表的长度。 对于m,我输入输入80 000 000
对于小数字,它的工作速度相对较快。如果我输入b > 50
;它返回28表示a,这是正确的。
答案 0 :(得分:3)
我的回答不是很漂亮或优雅,它仍然是蛮力。但是,它可以在一小段时间内简化问题空间并在不到10秒的时间内成功终止。
获取n:的因素
与@usethedeathstar提到的一样,可以测试最多n/2
的因子。但是,我们可以通过仅测试n:
let n = 36
=> factors(n) : (1x36, 2x18, 3x12, 4x9, 6x6, 9x4, 12x3, 18x2, 36x1)
正如你所看到的,它在6之后循环(36的平方根)。我们也不需要明确地返回因子,只需找出有多少...所以只需用sum()中的生成器计算它们:
import math
def get_factors(n):
return sum(2 for i in range(1, round(math.sqrt(n)+1)) if not n % i)
测试三角数
我使用了生成器函数来产生三角形数字:
def generate_triangles(limit):
l = 1
while l <= limit:
yield sum(range(l + 1))
l += 1
最后,开始测试:
def test_triangles():
triangles = generate_triangles(100000)
for i in triangles:
if get_factors(i) > 499:
return i
使用探查器运行它,它在不到10秒的时间内完成:
$ python3 -m cProfile euler12.py
361986 function calls in 8.006 seconds
此处节省的最大时间是get_factors(n)
仅测试n的平方根 - 这使得heeeaps更快,并且通过不生成因子列表来节省大量内存开销。
正如我所说,它仍然不漂亮 - 我相信有更优雅的解决方案。但是,它符合更快的要求:)
答案 1 :(得分:2)
import time
from math import sqrt
def count_divisors(n):
d = {}
count = 1
while n % 2 == 0:
n = n / 2
try:
d[2] += 1
except KeyError:
d[2] = 1
for i in range(3, int(sqrt(n+1)), 2):
while n % i == 0 and i != n:
n = n / i
try:
d[i] += 1
except KeyError:
d[i] = 1
d[n] = 1
for _,v in d.items():
count = count * (v + 1)
return count
def tri_number(num):
next = 1 + int(sqrt(1+(8 * num)))
return num + (next/2)
def main():
i = 1
while count_divisors(i) < 500:
i = tri_number(i)
return i
start = time.time()
answer = main()
elapsed = (time.time() - start)
print("result %s returned in %s seconds." % (answer, elapsed))
以下是显示时间增量和正确答案的输出:
$ python ./project012.py
result 76576500 returned in 1.82238006592 seconds.
为计算除数,我首先初始化一个空字典和一个计数器。对于找到的每个因子,如果值不存在,则创建d [factor]的键,其值为1;否则,将增加d [factor]的值。
例如,如果我们计算因子100,我们将看到d = {25:1,2:2}
第一个while循环,我分解出所有2,每次将n除以2。接下来,我从3开始分解,每次跳过两次(因为我们已经分解了所有偶数),并且一旦到达n + 1的平方根就停止。
我们在n的平方根处停止,因为如果存在一个对数,其中一个数大于n的平方根,则对中的另一个必须小于10。如果不存在较小的数,则存在没有匹配的较大因素。 https://math.stackexchange.com/questions/1343171/why-only-square-root-approach-to-check-number-is-prime
while n % 2 == 0:
n = n / 2
try:
d[2] += 1
except KeyError:
d[2] = 1
for i in range(3, int(sqrt(n+1)), 2):
while n % i == 0 and i != n:
n = n / i
try:
d[i] += 1
except KeyError:
d[i] = 1
d[n] = 1
现在我已经获得了每个因子,并将其添加到字典中,我们必须添加最后一个因子(仅为n)。
现在字典已完成,我们遍历每个项目,然后应用以下公式:d(n)=(a + 1)(b + 1)(c + 1)... https://www.wikihow.com/Determine-the-Number-of-Divisors-of-an-Integer
此公式的全部含义是将每个因子的所有计数取1,然后将它们相乘。以100为例,它具有因子25、2和2。我们将计算d(n)=(a + 1)(b + 1)=(1 + 1)(2 + 1)=(2)(3 )= 6个除数
for _,v in d.items():
count = count * (v + 1)
return count
现在,看一下tri_number(),您可以看到我选择了计算序列中的下一个三角形数,而无需手动将每个整数相加(节省了数百万次操作)。取而代之的是我使用T(n)= n(n + 1)/ 2 http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/runsums/triNbProof.html
我们正在为函数提供一个整数作为参数,因此我们需要求解n,这将是接下来要添加的整数。一旦有了下一个数字(n),我们只需将单个数字添加到num并返回
S = n(n + 1)2
S = n2 + n2
2S = n2 + n
n2 + n−2S = 0
这时,我们对ax2 + bx + c = 0使用二次公式。
n = −b±√b2-4ac/ 2a
n = −1±√1-4(1)(− 2S)/ 2
n = −1±√1+ 8S / 2
https://socratic.org/questions/how-do-you-solve-for-n-in-s-n-n-1-2
因此,所有tri_number()所做的运算均为n = 1 +√1+ 8S / 2(我们在这里忽略了否定方程)。返回的答案是序列中的下一个三角形。
def tri_number(num):
next = 1 + int(sqrt(1+(8 * num)))
return num + (next/2)
最后,我们可以看一下main()。我们从整数1开始。我们计算1的除数。如果它小于500,我们得到下一个三角形数,然后一次又一次尝试,直到得到大于500的数。
def main():
i = 1
while count_divisors(i) < 500:
i = tri_number(i)
return i
我确信还有其他优化方法,但我不够聪明,无法理解这些方法。如果您找到优化python的更好方法,请告诉我!我最初在Golang解决了第12个项目,并且运行时间为25毫秒!
$ go run project012.go
76576500
2018/07/12 01:56:31 TIME: main() took 23.581558ms
答案 2 :(得分:0)
我可以给出的一个提示是
def genfact(n):
t = []
for i in xrange(1, n+1):
if n%i == 0:
t.append(i)
return t
将其更改为
def genfact(n):
t=[]
for i in xrange(1,numpy.sqrt(n)+1):
if(n%i==0):
t.append(i)
t.apend(n/i)
因为如果a是除数而不是b = n / a,因为a * b = a * n / b = n,那应该有助于一部分(不确定在你的情况下是否可能是正方形,但是如果是这样,添加另一个案例以排除两次添加相同的数字)
你也可以设计一个递归的东西,(比如它是28之类的东西,你得到1,28,2,14,而在你知道14的那一刻,你输入一些东西来实际记住除数14(memoize),而不是检查它们是否在列表中是alraedy,如果没有,则将它们添加到列表中,同时为每个14的除数加上28 / d,最后只取出重复项
如果您认为我的第一个答案仍然不够快,请求更多,我将检查如何通过一些更多的技巧更快地解决它(可能会使用erastothenes筛子等等,以及一些如果你想把这个问题真的炸成很大的比例,比如用超过10k的除数检查第一个,那么也可以考虑其他技巧。
答案 3 :(得分:0)
while True:
c=0
n=1
m=1
for i in range(1,n+1):
if n%i==0:
c=c+1
m=m+1
n=m*(m+1)/2
if c>500:
break
print n
答案 4 :(得分:0)
这不是我的代码,但是已经过优化。 来源:http://code.jasonbhill.com/sage/project-euler-problem-12/
import time
def num_divisors(n):
if n % 2 == 0: n = n / 2
divisors = 1
count = 0
while n % 2 == 0:
count += 1
n = n / 2
divisors = divisors * (count + 1)
p = 3
while n != 1:
count = 0
while n % p == 0:
count += 1
n = n / p
divisors = divisors * (count + 1)
p += 2
return divisors
def find_triangular_index(factor_limit):
n = 1
lnum, rnum = num_divisors(n), num_divisors(n + 1)
while lnum * rnum < 500:
n += 1
lnum, rnum = rnum, num_divisors(n + 1)
return n
start = time.time()
index = find_triangular_index(500)
triangle = (index * (index + 1)) / 2
elapsed = (time.time() - start)
print("result %s returned in %s seconds." % (triangle, elapsed))