我正在写一个距离矩阵,最后我制作了以下代码
In [83]: import numpy as np
In [84]: np.set_printoptions(linewidth=120,precision=2)
In [85]: n = 7 ; a = np.arange(n) ; o = np.ones(n) ; np.sqrt(np.outer(o,a*a)+np.outer(a*a,o))
Out[85]:
array([[ 0. , 1. , 2. , 3. , 4. , 5. , 6. ],
[ 1. , 1.41, 2.24, 3.16, 4.12, 5.1 , 6.08],
[ 2. , 2.24, 2.83, 3.61, 4.47, 5.39, 6.32],
[ 3. , 3.16, 3.61, 4.24, 5. , 5.83, 6.71],
[ 4. , 4.12, 4.47, 5. , 5.66, 6.4 , 7.21],
[ 5. , 5.1 , 5.39, 5.83, 6.4 , 7.07, 7.81],
[ 6. , 6.08, 6.32, 6.71, 7.21, 7.81, 8.49]])
我告诉自己“你浪费了一个外在的产品,你这个傻瓜!保存其中一个并使用转置!”,我说写了
In [86]: n = 7 ; a = np.outer(np.arange(n)**2, np.ones(n)) ; np.sqrt(a+a.T)
Out[86]:
array([[ 0. , 1. , 2. , 3. , 4. , 5. , 6. ],
[ 1. , 1.41, 2.24, 3.16, 4.12, 5.1 , 6.08],
[ 2. , 2.24, 2.83, 3.61, 4.47, 5.39, 6.32],
[ 3. , 3.16, 3.61, 4.24, 5. , 5.83, 6.71],
[ 4. , 4.12, 4.47, 5. , 5.66, 6.4 , 7.21],
[ 5. , 5.1 , 5.39, 5.83, 6.4 , 7.07, 7.81],
[ 6. , 6.08, 6.32, 6.71, 7.21, 7.81, 8.49]])
到目前为止,这么好,我有两个(稍微)不同的同一个想法的实现,一个明显比另一个快,不是吗?
In [87]: %timeit n = 1001 ; a = np.arange(n) ; o = np.ones(n) ; np.sqrt(np.outer(o,a*a)+np.outer(a*a,o))
100 loops, best of 3: 13.7 ms per loop
In [88]: %timeit n = 1001 ; a = np.outer(np.arange(n)**2, np.ones(n)) ; np.sqrt(a+a.T)
10 loops, best of 3: 19.7 ms per loop
In [89]:
没有!更快的实施速度慢了50%!
我对刚刚发现的行为感到惊讶,我对此感到惊讶吗?从不同的角度来看,不同时间背后的理由是什么?
答案 0 :(得分:1)
重构代码以重用a
和o
,我得出相反的结论:
import timeit
import numpy as np
n = 1001
a = np.arange(n)
o = np.ones(n)
def g(a, o):
z = np.sqrt(np.outer(o,a*a)+np.outer(a*a,o))
def f(a, o):
a = np.outer(a**2, o)
y = np.sqrt(a+a.T)
assert np.all(f(a, o) == g(a, o))
t = Timer('g(a, o)', 'from __main__ import a, o, np, f, g')
print 'g:', t.timeit(100)/100 # g: 0.0166591598767
t = Timer('f(a, o)', 'from __main__ import a, o, np, f, g')
print 'f:', t.timeit(100)/100 # f: 0.0200494056252
答案 1 :(得分:1)
以下是小n=7
的一些时间安排:
In [784]: timeit np.outer(o,a*a)
10000 loops, best of 3: 24.2 µs per loop
In [785]: timeit np.outer(a*a,o)
10000 loops, best of 3: 25.7 µs per loop
In [786]: timeit np.outer(a*a,o)+np.outer(o,a*a)
10000 loops, best of 3: 52.7 µs per loop
2 outers在同一时间,他们的总和比他们的总时间多一点。
In [787]: timeit a2=np.outer(a*a,o); a2+a2.T
10000 loops, best of 3: 33.2 µs per loop
In [788]: timeit a2=np.outer(a*a,o); a2+a2
10000 loops, best of 3: 27.9 µs per loop
In [795]: timeit a2=np.outer(a*a,o); a2.T+a2.T
10000 loops, best of 3: 29.4 µs per loop
比较这些2,我们发现将a2.T
添加到a2
比将a2
添加到自身,甚至a2.T
添加到自身要慢。执行转置是便宜的,只需改变形状和步幅。但是混合步幅的迭代速度较慢。迭代器甚至可以使用临时缓冲区。
因此,在我的计划中,预先计算outer
相同的时间,但不会像人们预期的那样多。
对于大型n
,2 (n,n)
个数组的总和大约与生成它们的次数相同。因此,预先计算outer
的相对优势会降低。
先前outer
和a*a.T
的比较已被忽略。
答案 2 :(得分:1)
执行你的例子很有趣,我得到了相反的结果:
In [7]: %timeit n = 1001 ; a = np.arange(n) ; o = np.ones(n) ; np.sqrt(np.outer(o,a*a)+np.outer(a*a,o))
100 loops, best of 3: 17.2 ms per loop
In [8]: %timeit n = 1001 ; a = np.outer(np.arange(n)**2, np.ones(n)) ; np.sqrt(a+a.T)
100 loops, best of 3: 12.8 ms per loop
但这是我能想到的最快最简单的方法:
In [139]: %timeit n = 1001 ; a = np.arange(n); np.sqrt((a**2)[:, np.newaxis]+a**2)
100 loops, best of 3: 10.8 ms per loop
顺便说一句,如果您正在处理距离,您可能会发现scipy.spatial.distance
模块和scipy.spatial.distance_matrix
函数很有用。