两个n维向量的点积u=[u1,u2,...un]和v=[v1,v2,...,vn]被由下式给出u1*v1 + u2*v2 + ... + un*vn。
u=[u1,u2,...un]
v=[v1,v2,...,vn]
u1*v1 + u2*v2 + ... + un*vn
昨天发布的一个问题鼓励我找到仅使用标准库而不使用第三方模块或C / Fortran / C ++调用的Python中计算点积的最快方法。
我选择了四种不同的方法。到目前为止,最快的似乎是sum(starmap(mul,izip(v1,v2)))(来自模块的位置starmap和位置)。izip``itertools
sum(starmap(mul,izip(v1,v2)))
starmap
izip``itertools
对于下面显示的代码,以下是经过的时间(百万次运行,以秒为单位):
d0: 12.01215 d1: 11.76151 d2: 12.54092 d3: 09.58523
您能想到一种更快的方法吗?
import timeit # module with timing subroutines import random # module to generate random numnbers from itertools import imap,starmap,izip from operator import mul def v(N=50,min=-10,max=10): """Generates a random vector (in an array) of dimension N; the values are integers in the range [min,max].""" out = [] for k in range(N): out.append(random.randint(min,max)) return out def check(v1,v2): if len(v1)!=len(v2): raise ValueError,"the lenght of both arrays must be the same" pass def d0(v1,v2): """ d0 is Nominal approach: multiply/add in a loop """ check(v1,v2) out = 0 for k in range(len(v1)): out += v1[k] * v2[k] return out def d1(v1,v2): """ d1 uses an imap (from itertools) """ check(v1,v2) return sum(imap(mul,v1,v2)) def d2(v1,v2): """ d2 uses a conventional map """ check(v1,v2) return sum(map(mul,v1,v2)) def d3(v1,v2): """ d3 uses a starmap (itertools) to apply the mul operator on an izipped (v1,v2) """ check(v1,v2) return sum(starmap(mul,izip(v1,v2))) # generate the test vectors v1 = v() v2 = v() if __name__ == '__main__': # Generate two test vectors of dimension N t0 = timeit.Timer("d0(v1,v2)","from dot_product import d0,v1,v2") t1 = timeit.Timer("d1(v1,v2)","from dot_product import d1,v1,v2") t2 = timeit.Timer("d2(v1,v2)","from dot_product import d2,v1,v2") t3 = timeit.Timer("d3(v1,v2)","from dot_product import d3,v1,v2") print "d0 elapsed: ", t0.timeit() print "d1 elapsed: ", t1.timeit() print "d2 elapsed: ", t2.timeit() print "d3 elapsed: ", t3.timeit()
注意,文件名必须是dot_product.py脚本运行的名称。我在Mac OS X版本10.5.8上使用了Python 2.5.1。
dot_product.py
编辑:
我为N = 1000运行了脚本,结果如下(以秒为单位,运行一百万次):
d0: 205.35457 d1: 208.13006 d2: 230.07463 d3: 155.29670
我想可以肯定地说,选项三是最快的,而选项二是最慢的(在所提出的四个选项中)。
我只是为了好玩而写了一个使用numpy的“ d4”:
from numpy import dot def d4(v1, v2): check(v1, v2) return dot(v1, v2)
我的结果(Python 2.5.1,XP Pro sp3、2GHz Core2 Duo T7200):
d0 elapsed: 12.1977242918 d1 elapsed: 13.885232341 d2 elapsed: 13.7929552499 d3 elapsed: 11.0952246724
过去了d4:56.3278584289#变得麻木了!
而且,为了获得更多乐趣,我打开了psyco:
d0 elapsed: 0.965477735299 d1 elapsed: 12.5354792299 d2 elapsed: 12.9748163524 d3 elapsed: 9.78255448667
过去的d4:54.4599059378
基于此,我宣布d0为赢家:)
更新资料
@ kaiser.se:我可能应该提到我确实先将所有内容转换为numpy数组:
from numpy import array v3 = [array(vec) for vec in v1] v4 = [array(vec) for vec in v2] # then t4 = timeit.Timer("d4(v3,v4)","from dot_product import d4,v3,v4")
我包括了,check(v1, v2)因为它包含在其他测试中。放弃它会给numpy一个不公平的优势(尽管看起来它可以使用一个)。数组转换减少了大约一秒钟(比我想象的要少得多)。
check(v1, v2)
我所有的测试均以N = 50进行。
@nikow:我使用的numpy 1.0.4无疑是旧的,自从我安装了numpy以来,它们在过去一年半的时间里肯定有提高的表现。
更新#2
@ kaiser.se哇,您完全正确。我一定一直以为这些是列表列表或其他内容(我真的不知道我在想什么……对编程为+1)。
看起来如何:
v3 = array(v1) v4 = array(v2)
新结果:
d4 elapsed: 3.22535741274
使用Psyco:
d4 elapsed: 2.09182619579
d0仍然可以通过Psyco获胜,但是numpy总体上可能更好,尤其是对于较大的数据集。
昨天,我对缓慢的numpy结果感到不安,因为numpy可能用于 大量 计算并且已经进行了很多优化。显然,尽管如此,我还是不愿意去检查我的结果:)