优化 Python 代码以提高效率

3

对这段代码进行分析,发现大部分时间都用在了日志操作上。有没有其他更高效的Python 3写法呢?使用列表推导式代替循环或者使用map函数因为需要使用lambda表达式而导致效率更低。

def log_total(data): 
    total = 0.0   
    log = log(data) 
    for i in range(10000): 
        total += log/(i+1)  
    return total

谢谢!


5
你真的需要选择与“log”函数相同的变量名吗? - Matt Ball
2
你可以节省一些计算量:for i in range(1,10001): total += log/i - mgilson
1
你的 log 函数是从 math 模块中来的吗?如果是,那么我怀疑你直接改进它的性能可能会有困难(因为它是一个用 C 实现的内置函数)。如果 log 是你自己编写的,那么可能还有改进的空间,但在我们提出建议之前,你需要展示它的代码! - Blckknght
@Blckknght,我确实在使用内置的日志函数,但我在考虑使用另一个可能的库来加速。然而,mglison的技巧让我获得了很大的速度提升。 - user_noname_00
@user_noname_00 -- 你还可以通过设置默认参数来获得另一个(非常小的)速度提升:def log_total(data,log=log):,这样可以使log函数的查找局限于您的函数而不是全局命名空间。但是,这只是一个相当微小的加速,只有在您能够证明它确实有所作用时才应该使用(然后需要大量的文档说明正在发生什么)。 - mgilson
显示剩余2条评论
2个回答

9

我建议你把log从求和式中提取出来并缓存你的求和结果:

harmonic_series = sum(1. / i for i in range(1, 10001))  # Thanks, @mgilson

def log_total(data): 
    return log(data) * harmonic_series

你可以使用PyPy来进一步提高速度。

@user_noname_00:看看我的编辑。它真的不可能变得更慢。 - Blender
像这样进行缓存确实非常快,但就像作弊一样:) 必须有一种方法在函数内部进行优化。当我说它速度较慢时,总和是在函数内计算的。 - user_noname_00
1
但是,这些解决方案是等效的。即使您优化了 log,它仍然会被调用 10,000 次,因此每次都会产生巨大的成本,特别是当您实际上使用静态变量但在每次调用方法时计算它时。下一步将是使用其他问题中提到的 memmoization 从函数中获得更快的速度。 - sean
4
所以您希望优化它,但它需要保持缓慢? - Blender
你也可以使用numpy更好地完成这个任务:(1./np.arange(1,10001)).sum() 对我来说可以将其缩短约6倍,但不是那么懒惰。 - mgilson
如果您使用的是Python 2.x,并且坚持在log_total内计算总和,请将range更改为xrange,以避免每次创建10,000个元素列表。(但实际上,这个1+1/2+1/3+...+1/10000的总和是不变的,应该从循环函数中提取出来。也许一个妥协是将其作为log_total的默认参数包含在内,这样它就只会在编译时计算一次,而不是在每次调用log_total时都计算。) - PaulMcG

0
可以用一行代码写成lambda,像这样:
total = lambda data: log(data) * sum(1.0 / i for i in xrange(1, 10001))

我使用的是Python 2.7.3


我改了它。我忘记了一个 * b1 + a * b2 + a * bn = a * (b1 + b2 + bn)。 - user1389938
哈哈,我忘记写了。 - user1389938
1
将您的列表推导式更改为生成器。通过添加括号,会创建一个实际包含10000个元素的列表(尝试将10001更改为1000000001并查看发生了什么),这将占用大量RAM。 - Blender
你的代码片段正在进行整数除法。请改为使用1.0/i或者使用from __future__ import division,否则你的总和将等于1+0+0+0+... - PaulMcG
@Paul McGuire 谢谢! 我学习了 from__future__ import division - user1389938

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接