【问题标题】:Possible to calculate a double sum in python using list comprehensions to replace both for-loops?可以使用列表推导在 python 中计算双倍总和来替换两个 for 循环吗?
【发布时间】:2018-01-27 10:33:17
【问题描述】:

我有一个双数,基本上是这样的

sum = exp(x^2 + y^2)

当然,我可以简单地使用两个嵌套的for 循环,但对于大数字来说这往往很耗时。我可以使用一个列表理解来替换内部 for 循环,请参见此处:

import numpy as np

N_x      = 100
N_y      = 100

# straight forward way
result_1 = .0
for x in xrange(N_x):
    for y in xrange(N_y):
        result_1 += np.exp( (float(x)/N_x)**2 + ( (float(y)/N_y)**2 )

# using one list comprehension
result_2 = .0
for x in xrange(N_x):
    inner_loop = [ np.exp( (float(y)/N_y)**2 ) for y in range(N_y) ]
    result_2  += np.exp( (float(x)/N_x)**2 ) * sum(inner_loop)

但是如何用列表理解(我希望它更快)替换外部 for 循环,有什么提示吗?

【问题讨论】:

    标签: python numpy list-comprehension


    【解决方案1】:

    你为什么不以 numpy 的方式来做......没有 for 循环:

    x = np.arange(N_x)
    y = np.arange(N_y)
    
    xx, yy = np.meshgrid(x, y)
    result = np.sum(np.exp((xx/N_x)**2 + (yy/N_y)**2))
    

    【讨论】:

    • 我只是说 OP 问题首先要求计算 sum = exp(x^2 + y^2),但他们想要修改的代码实际上并没有计算这个总和,它做更多...
    • 来想一想,我猜op方程其实和代码是一致的,我只是糊涂了……
    • 是的,完全摆脱循环很有意义
    • 哇,这样更快,谢谢! (而且,是的,实际代码做得更多)。
    【解决方案2】:
    result = sum(np.exp(x**2 + y**2) for x in range(N_x) for y in range(N_y))
    

    【讨论】:

    • 嗯,这又快又容易:)
    • result == result_1--> False
    • @MikeMüller 我已将参数简化为np.exp。答案的目的是说明如何在单行表达式中编写两个循环。
    • @SilverSlash 确实如此,尽管我现在意识到这可能不是最有效的方式......
    【解决方案3】:

    你快到了。完整的和可以写成两个一维和的乘积,即(sum exp x^2) * (sum exp y^2):

    >>> import numpy as np                      
    >>>                       
    >>> N_x = N_y = 100                                      
    >>> 
    # brute force                         
    >>> result_1 = .0
    >>> for x in xrange(N_x):
    ...     for y in xrange(N_y):
    ...         result_1 += np.exp( (float(x)/N_x)**2 + (float(y)/N_y)**2 )
    ... 
    >>> result_1
    21144.232143358553
    >>> 
    # single product method
    >>> from __future__ import division
    >>> 
    >>> x, y = np.arange(N_x) / N_x, np.arange(N_y) / N_y
    >>> np.exp(x*x).sum() * np.exp(y*y).sum()
    21144.232143358469
    

    我的猜测是,您甚至可以使用 list comp 并击败蛮力 numpy 方法:

    >> rx, ry = 1.0 / (N_x*N_x), 1.0 / (N_y*N_y)
    >>> sum([np.exp(rx*x*x) for x in xrange(N_x)]) * sum([np.exp(ry*y*y) for y in xrange(N_y)])
    21144.232143358469
    

    确实,在 Python3 中完成了计时,因为我不知道如何在 Python2 中使用timeit:

    >>> from timeit import repeat
    >>> 
    >>> kwds = dict(globals=globals(), number=100)
    >>> 
    # single product - list comp
    >>> repeat('sum(np.exp(rx*x*x) for x in range(N_x)) * sum(np.exp(ry*y*y) for y in range(N_y))', **kwds)
    [0.0166887859813869, 0.016465034103021026, 0.016357041895389557]
    >>>
    # numpy brute force
    >>> repeat('np.exp(np.add.outer(x*x, y*y)).sum()', **kwds)
    [0.07063774298876524, 0.0348161740694195, 0.02283189189620316]
    

    显然,numpy 单品更快

    >>> repeat('np.exp(x*x).sum() * np.exp(y*y).sum()', **kwds)
    [0.0031406711786985397, 0.0031003099866211414, 0.0031157969497144222]
    

    【讨论】:

    • 当然,您的代码比其他代码更快。在我的电脑中,[Paul Panzer] 每个循环 33.4 µs ± 454 ns,[Gerges Dib] 每个循环 430 µs ± 5.33 µs,[SilverSlash] 每个循环 18.5 ms ± 343 µs。所有代码都比原来的快。
    • @ChoF 感谢您的时间安排!
    猜你喜欢
    • 2011-02-20
    • 2020-11-24
    • 2021-08-19
    • 2020-10-31
    • 2021-10-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    相关资源
    最近更新 更多