Produkt punktowy dwóch n-wymiarowych wektorów u=[u1,u2,...un]
i v=[v1,v2,...,vn]
jest podany przez u1*v1 + u2*v2 + ... + un*vn
.Zoptymalizowany produkt dotowy w Pythonie
Pytanie posted yesterday zachęciło mnie do znalezienia najszybszego sposobu obliczania produktów dot w Pythonie przy użyciu tylko standardowej biblioteki, bez modułów firm trzecich lub wywołań C/Fortran/C++.
Wyliczyłem cztery różne podejścia; jak dotąd najszybszym wydaje się być sum(starmap(mul,izip(v1,v2)))
(gdzie starmap
i izip
pochodzą z modułu itertools
).
Dla kodu przedstawionego poniżej, są minęły czasy (w sekundach, na milion przebiegów):
d0: 12.01215
d1: 11.76151
d2: 12.54092
d3: 09.58523
można myśleć szybszy sposób to zrobić?
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()
Należy zauważyć, że nazwa pliku musi być dot_product.py
dla skryptu do uruchomienia; Użyłem Pythona 2.5.1 na Mac OS X wersja 10.5.8.
EDIT:
wpadłem skryptu dla n = 1000 i są to wyniki (w sekundach, dla jednego miliona przejazdów):
d0: 205.35457
d1: 208.13006
d2: 230.07463
d3: 155.29670
myślę, że można bezpiecznie przyjąć, że rzeczywiście , opcja trzecia jest najszybsza, a opcja druga najwolniejsza (z czterech prezentowanych).
@Arrieta: Można usunąć wymaganie, aby plik był nazywany dot_product.py, zastępując "z dot_product" przez "od __main__". – unutbu
@unutbu: Oczywiście, po prostu myślę, że łatwiej jest zapisać plik o tej nazwie do szybkiego uruchomienia niż zmienić skrypt. Dziękuję Ci. – Escualo
Moje wyniki: d0: 13.4328830242 d1: 9.52215504646 d2: 10.1050257683 d3: 9.16764998436 Należy sprawdzić, czy różnice między d1 i d3 są statystycznie istotne. – liori