Python效率/优化项目Euler#5示例

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 &

我写了这个解决方案

我的系统大约需要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 < 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)