【问题标题】:Triple for-loop in a vector向量中的三重for循环
【发布时间】:2021-04-17 11:40:16
【问题描述】:

我有一个简单的 numpy 数组 (3xN),例如:

v = np.array([[-3.33829, -3.42467, -3.53332],
   [-2.67681, -2.6082 , -3.49502],
   [-3.49497, -2.73177, -2.61499],
   [-2.76056, -3.57753, -2.67334],
   [-1.96801, -3.47521, -3.51974],
   [-1.25571, -2.69451, -3.45554],
   [-1.94568, -2.59504, -2.72568],
   [-1.28991, -3.47927, -2.73176],
   [-0.51201, -3.50684, -3.40448],
   [ 0.22398, -2.70244, -3.43421]])

这里,N = 10,但在我的实际情况下,它比这里 (+500) 大得多。每行都是一个点 - 欧几里得坐标。

我想执行:

其中 i、j 和 k 表示与 v 不同的行。

如何在 Python 上快速实现它?

【问题讨论】:

  • 500 没什么大不了的。这种性能很关键吗? for 循环去 brrrr
  • 这是 500^3 所以这可能是个大问题... 3 代表 3 for loops = 125,000,000
  • 查看numpy.einsum。它旨在处理此类情况。
  • 在这种情况下,预期输出的形状是什么?我会按照等式假设3XN
  • 虽然看起来像欧式距离,但实际上不是坐标之间的差异,而是向量之间的差异。我可能是错的,但对我来说,等式表明相同。

标签: python-3.x numpy for-loop


【解决方案1】:

您可以使用 numpy broadcasting 操作来做到这一点:

diffs = ((v[:, None] - v) ** 2).sum(-1)
d = np.exp(diffs + diffs[:, None]).sum((0, 1))
print(d)

# [3.08316899e+11 2.37020625e+07 4.05357364e+12 8.22697743e+08
#  8.85209202e+04 2.55340202e+05 7.33879459e+04 1.88175133e+05
#  8.10134295e+08 6.62122925e+12]

即使对于大小为 500 的数组,也只需几秒钟即可计算出结果:

%%time
v = np.random.rand(500, 3)
diffs = np.sum((v[:, None] - v) ** 2, -1)
d = np.exp(diffs + diffs[:, None]).sum((0, 1))

# CPU times: user 2.74 s, sys: 5.5 ms, total: 2.75 s
# Wall time: 2.75 s

【讨论】:

    【解决方案2】:

    IIUC,该等式表明成对的向量差异,而不是向量之间的平方距离。

    N 个向量之间的成对差将是 N*N 个向量。

    最后,我假设因为您只减少了 jk 轴,所以输出向量是 (10,3) 而不是 (10,)。如果我错了,请纠正我。

    import numpy as np
    
    d = np.exp(((v[:,None]-v)**2)[:,None] + ((v[:,None]-v)**2)).sum((0,1))
    print(d)
    
    #### Stepwise breakdown
    #v                           #i,3 -> 10,3
    #diff = (v[:,None]-v)**2     #j,i,3 -> 10,10,3
    #power = diff[:,None]+diff   #k,j,i,3 -> 10,10,10,3
    #exp = np.exp(power)         #k,j,i,3 -> 10,10,10,3
    #d = np.sum(exp,(1,2))       #i,3 -> 10,3
    
    array([[4.38558108e+11, 2.11224470e+02, 2.08153285e+02],
           [6.10332697e+09, 2.42309774e+02, 2.00079357e+02],
           [1.37237360e+12, 2.11552094e+02, 2.32739462e+02],
           [9.98934092e+09, 2.51158071e+02, 2.16562340e+02],
           [1.77827910e+08, 2.22151678e+02, 2.05163797e+02],
           [1.91234145e+08, 2.19457894e+02, 1.92858561e+02],
           [1.63391357e+08, 2.46419838e+02, 2.04498335e+02],
           [1.67512751e+08, 2.23119070e+02, 2.03232700e+02],
           [8.45322705e+09, 2.30065042e+02, 1.85024981e+02],
           [1.14468558e+12, 2.17683864e+02, 1.89388595e+02]])
    

    基准测试-

    %%timeit
    np.exp(((v[:,None]-v)**2)[:,None] + ((v[:,None]-v)**2)).sum((0,1))
    # 21.2 s ± 3.27 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    【讨论】:

    • “该等式表明成对向量差异,而不是向量之间的平方距离。”实际上,平方差是向量之间的平方距离。正方形表示内积。因此,结果应该是一个数组 (10,)。感谢您的评论。
    猜你喜欢
    • 2015-01-24
    • 2014-03-15
    • 2023-02-08
    • 2016-03-11
    • 1970-01-01
    • 2013-07-31
    • 2023-02-16
    • 2019-11-23
    • 1970-01-01
    相关资源
    最近更新 更多