【问题标题】:Speed up double loop in Python在 Python 中加速双循环
【发布时间】:2013-07-18 05:06:40
【问题描述】:

有没有一种方法可以加快从上一次迭代更新其值的双循环?

在代码中:

def calc(N, m):
    x = 1.0
    y = 2.0
    container = np.zeros((N, 2))
    for i in range(N):
      for j in range(m):
        x=np.random.gamma(3,1.0/(y*y+4))
        y=np.random.normal(1.0/(x+1),1.0/sqrt(x+1))
      container[i, 0] = x
      container[i, 1] = y
    return container

calc(10, 5)

如您所见,内循环正在更新变量 x 和 y,而外循环每次都以不同的 x 值开始。我不认为这是可矢量化的,但也许还有其他可能的改进。

谢谢!

【问题讨论】:

  • “第二个循环正在更新变量 x 和 y,而第一个循环每次都以不同的 x 值开始” - 我不确定你的意思。特别是,我不知道“每次以不同的 x 值开始”是什么意思,与“内部”或“外部”相比,“第一个”或“第二个”循环相当不清楚。我不完全确定哪个是哪个。
  • 抱歉,我想不出比用xrange 替换range 更多的事情了。也许你应该悬赏这个问题来吸引numpy 专家
  • 对不起。你说得对。我将更改第一个或第二个循环的事情。 每次都以不同的 x 值开始,我的意思是外循环的第一次迭代以 x = 1.0 开始。但是,x 在内部循环中被修改。因此,在外循环的第二次迭代中,x 是另一个值。
  • @inspectorG4dget 没问题。感谢您的帮助。

标签: python loops optimization numpy micro-optimization


【解决方案1】:

这行得通吗?

for i in xrange(N): 
   # xrange is an iterator, range makes a new list.
   # You save linear space and `malloc`ing time by doing this

    x += m*y # a simple algebra hack. Compute this line of the loop just once instead of `m` times
    y -= m*x
    y *= -1 # another simple algebra hack. Compute this line of the loop just once instead of `m` times
    container[i,0] = x
    container[i,1] = y
return container

【讨论】:

  • 感谢您的回答。 xrange 是个好主意。但是,代数部分不适用于我正在尝试优化的真实循环(它涉及 gamma 函数)
  • @RobertSmith:抱歉,我现在想不到太多其他的了
【解决方案2】:

我认为它不会增加任何重要的加速,但如果您一次生成所有 gamma 和正态分布的随机值,则可以节省一些函数调用。

Gamma 函数具有 scaling property,因此如果您从 gamma(k, 1) 分布中提取值 x,则 c*x 将是从 gamma(k, c) 分布中提取的值。类似地,对于正态分布,您可以从正态 (0, 1) 分布中提取 y 值,并将其转换为从正态 (m, s) 分布中提取的值,执行 x*s + m。所以你可以重写你的函数如下:

def calc(N, m):
    x = 1.0
    y = 2.0
    container = np.zeros((N, 2))
    nm = N*m
    gamma_vals = np.random.gamma(3, 1, size=(nm,))
    norm_vals = np.random.normal(0, 1, size=(nm,))
    for i in xrange(N):
        for j in xrange(m):
            ij = i*j
            x = gamma_vals[ij] / (y*y+4)
            y = norm_vals[ij]/np.sqrt(x+1) + 1/(x+1)
        container[i, 0] = x
        container[i, 1] = y
    return container

如果你的分布的实际参数有一个更简单的表达式,你实际上可以使用np.cumprod 或类似的一些复杂的形式,并且不用循环。我无法弄清楚这样做的方法......

【讨论】:

  • 不幸的是,这比在内循环中使用 np.random.gamma 和 np.random.normal 的方法慢 2 倍。也许有一个错误,因为它没有给出相同的结果。
  • 您正在绘制随机数,因此结果不同也就不足为奇了。即使您播种了随机数生成器,您也以不同的顺序使用它们,所以再一次,非常正常。至于性能,如果它允许删除循环,这将是一个巧妙的技巧,这对于您的问题来说似乎是不可能的。
  • 1+。你说得对。谢谢。你认为没有办法进一步优化吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-18
  • 2021-01-30
  • 2011-05-14
相关资源
最近更新 更多