Python效率/优化项目Euler#5示例
我写了这个解决方案 我的系统大约需要37秒 虽然我不必要地检查了3、4、5、6、7、8、9、10和12的整除性,但我的代码速度大约快了4倍 我是python新手,很难看出效率低下的原因 编辑: 我做了另一个测试Python效率/优化项目Euler#5示例,python,performance,Python,Performance,我写了这个解决方案 我的系统大约需要37秒 虽然我不必要地检查了3、4、5、6、7、8、9、10和12的整除性,但我的代码速度大约快了4倍 我是python新手,很难看出效率低下的原因 编辑: 我做了另一个测试 import time start_time = time.time() def ProjectEulerFive (m = 20): ls = [11, 13, 14, 15, 16, 17, 18, 19] a = m i = 0 while i &
import time
start_time = time.time()
def ProjectEulerFive (m = 20):
ls = [11, 13, 14, 15, 16, 17, 18, 19]
a = m
i = 0
while i < len(ls):
if ( a % ls[i]) != 0:
a += m
i = 0
continue
else:
i += 1
return a
print ProjectEulerFive();
print "took " + str(time.time() - start_time ) + " seconds"
导入时间
开始时间=time.time()
def ProjectEulerFive(m=20):
ls=[11,13,14,15,16,17,18,19]
a=m
i=0
而我
我的系统需要6秒,但是:
import time
start_time = time.time()
def ProjectEulerFive (m = 20):
a = m
start = 11
b = start
while b < m:
if ( a % b) != 0:
a += m
b = start
continue
else:
b += 1
return a
print ProjectEulerFive()
print "took " + str(time.time() - start_time ) + " seconds"
导入时间
开始时间=time.time()
def ProjectEulerFive(m=20):
a=m
开始=11
b=开始
而b
大约需要3.7秒不是对您的问题的回答(因此是社区wiki),但这里有一个用于计时功能的有用装饰器:
from functools import wraps
import time
def print_time(f):
@wraps(f)
def wrapper(*args, **kwargs):
t0 = time.time()
result = f(*args, **kwargs)
print "{0} took {1}s".format(f.__name__, time.time() - t0)
return result
return wrapper
用法如下:
@print_time
def foo(x, y):
time.sleep(1)
return x + y
在实践中:
>>> foo(1, 2)
foo took 1.0s
3
这应该不需要太多时间:
def gcd(a, b):
if (b == 0): return a
else: return gcd(b, a%b)
def lcm(a, b):
return abs(a*b) / gcd(a, b)
def euler5(n):
if (n == 1): return 1
else: return lcm(n, euler5(n-1))
print euler5(20)
我看到,虽然发布了一个更快的解决方案,但实际上没有人回答这个问题。事实上,这是一个很难回答的问题!最基本的解释是函数调用相对昂贵。不过,为了使这个结论具有说服力,我必须深入研究Python的内部结构。准备好 首先,我将使用从“优化”解决方案中反汇编(您的第三个版本)
ProjectEulerFive
和find_solution
。这里有很多内容,但只需快速扫描即可确认您的代码根本不调用任何函数:
很快就会明白:(a)这段代码不那么复杂,但(b)它还调用了三个不同的函数。第一个调用只是对xrange
的一个调用,但其他两个调用出现在最外层的for循环中。第一个调用是调用all
;第二个,我怀疑是生成器表达式的next
方法被调用。但是函数是什么并不重要;重要的是它们在循环中被调用
现在,你可能会想“有什么大不了的?”这里。这只是一个函数调用;这里或那里几纳秒——对吗?但事实上,这些纳秒加起来。由于最外层的循环总共通过232792560/20==11639628
循环,因此任何开销都会乘以1100多万。使用ipython
中的%timeit
命令快速计时表明,在我的机器上,函数调用(全部由它自己完成)的成本约为120纳秒:
>>> def no_call():
... pass
...
>>> def calling():
... no_call()
...
>>> %timeit no_call()
10000000 loops, best of 3: 107 ns per loop
>>> %timeit calling()
1000000 loops, best of 3: 235 ns per loop
232792560
took 2.40848493576 seconds
因此,对于while循环中出现的每个函数调用,都需要花费更多的时间120纳秒*11000000=1.32秒。如果第二个函数调用是对生成器表达式的next
方法的调用,那么该函数会被调用更多次,在genex中的每次迭代中调用一次——每个循环平均调用3-4次
现在来验证这个假设。如果函数调用是问题所在,那么消除函数调用就是解决方案。让我们看看
def find_solution(step):
for num in xrange(step, 999999999, step):
for n in check_list:
if num % n != 0:
break
else:
return num
return None
下面是一个版本的find_solution
,它几乎与原始版本使用for/else
语法所做的一样。唯一的函数调用是外部调用,指向xrange
,这不会导致任何问题。现在,当我为原始版本计时时,花了11秒:
found an answer: 232792560
took 11.2349967957 seconds
让我们看看这个新的改进版能管理什么:
found an answer: 232792560
took 2.12648200989 seconds
这比我的机器上最快版本的ProjectEulerFive
的性能快了一毛:
>>> def no_call():
... pass
...
>>> def calling():
... no_call()
...
>>> %timeit no_call()
10000000 loops, best of 3: 107 ns per loop
>>> %timeit calling()
1000000 loops, best of 3: 235 ns per loop
232792560
took 2.40848493576 seconds
一切都有意义了 您可以使用基本因子解决此问题。求解0.0004s中的n=20和0.0011中的n=50
from math import sqrt
import time
num = int(input("Number: "))
start_time = time.clock()
def is_prime(n):
if(n == 2 or n == 3):
return True
elif(n < 2 or n % 2 == 0):
return False
for i in range(3, int(sqrt(n))+1, 2):
if(n % i == 0):
return False
return True
def decompose(n):
if(n == 1 or n == 0):
return [n]
l = []
residue = n
while(residue != 1):
for i in range(1, residue+1):
if(residue%i==0 and is_prime(i)):
l.append(i)
residue //= i
break
return l
l = []
for i in range(1, num):
d = decompose(i)
for n in d:
if(l.count(n) < d.count(n)):
l += [n]*(d.count(n)-l.count(n))
result = 1
for i in l:
result*=i
print(result)
print("time: ", time.clock()-start_time)
从数学导入sqrt
导入时间
num=int(输入(“数字:”)
开始时间=time.clock()
def是_prime(n):
如果(n==2或n==3):
返回真值
elif(n<2或n%2==0):
返回错误
对于范围(3,int(sqrt(n))+1,2)内的i:
如果(n%i==0):
返回错误
返回真值
def分解(n):
如果(n==1或n==0):
返回[n]
l=[]
残基=n
而(剩余量!=1):
对于范围(1,残留物+1)内的i:
如果(余数%i==0且为_素数(i)):
l、 附加(i)
剩余量/=i
打破
返回l
l=[]
对于范围内的i(1,num):
d=分解(i)
对于d中的n:
如果(l.count(n)
这是我的实现
我理解其中的原因,但希望能从背后的数学中得到启发
如果我写下所有不大于最高可除数的素数,然后替换因子子集,其中包括小于我的极限的素数因子
from functools import reduce
def divisible_by_all_up_to(limit):
def is_prime(num):
if num == 2 or num == 3:
return True
if num % 2 == 0 or num < 2:
return False
for i in range(3, num):
if num % i == 0:
return False
return True
primes = [i for i in range(limit) if is_prime(i) == True]
mult = []
for index, value in enumerate(primes):
while value * value < limit:
value = value * value
mult += [value]
return mult
ans = divisible_by_all_up_to(20)
resp = reduce(lambda x, y: x*y, ans)
从functools导入reduce
def可除以所有(上限):
def是_prime(num):
如果num==2或num==3:
返回真值
如果num%2==0或num<2:
返回错误
对于范围内的i(3,num):
如果num%i==0:
返回错误
返回真值
素数=[i对于范围内的i(限制),如果是_素数(i)==True]
mult=[]
对于索引,枚举中的值(素数):
当值*值<限制时:
值=值*值
mult+=[值]
回程骡子
ans=可被所有(20)整除
响应=
from math import sqrt
import time
num = int(input("Number: "))
start_time = time.clock()
def is_prime(n):
if(n == 2 or n == 3):
return True
elif(n < 2 or n % 2 == 0):
return False
for i in range(3, int(sqrt(n))+1, 2):
if(n % i == 0):
return False
return True
def decompose(n):
if(n == 1 or n == 0):
return [n]
l = []
residue = n
while(residue != 1):
for i in range(1, residue+1):
if(residue%i==0 and is_prime(i)):
l.append(i)
residue //= i
break
return l
l = []
for i in range(1, num):
d = decompose(i)
for n in d:
if(l.count(n) < d.count(n)):
l += [n]*(d.count(n)-l.count(n))
result = 1
for i in l:
result*=i
print(result)
print("time: ", time.clock()-start_time)
from functools import reduce
def divisible_by_all_up_to(limit):
def is_prime(num):
if num == 2 or num == 3:
return True
if num % 2 == 0 or num < 2:
return False
for i in range(3, num):
if num % i == 0:
return False
return True
primes = [i for i in range(limit) if is_prime(i) == True]
mult = []
for index, value in enumerate(primes):
while value * value < limit:
value = value * value
mult += [value]
return mult
ans = divisible_by_all_up_to(20)
resp = reduce(lambda x, y: x*y, ans)