Используйте Формула Эйлера . Таким образом, мы можем уменьшить количество дорогостоящих вычислений функций с n^2
до 2n
(если считать сложные функции в два раза). То, что мы делаем, выражаем синус внешнего различия как внешний продукт сложных экспонент. Поскольку умножение намного дешевле синуса, это приводит к хорошему net ускорению:
import numpy as np
from timeit import timeit
def pp():
T = np.exp(1j*theta)
T = np.outer(T,T.conj()).imag
return T
def orig():
T = theta[:, None] - theta
T = np.sin(T)
return T
rng = np.random.default_rng()
theta = rng.uniform(-np.pi,np.pi,1000)
print(np.allclose(orig(),pp()))
print(timeit(orig,number=10))
print(timeit(pp,number=10))
Пример выполнения:
True # results are the same
0.2818465740419924 # original method
0.04591922601684928 # Euler
Ускорение (зависит от количества элементов тета, здесь 1000) ~ 6x
В зависимости от формы K
(скаляр, 1D или 2D) мы можем оптимизировать далее:
def pp():
T = np.exp(1j*theta)
if K.ndim == 0:
A = T*(T.sum().conj()*K)
elif K.ndim == 1:
A = T * (K@T.conj())
else:
A = T * (K@T.conj())
return A.imag
def orig():
T = theta[:, None] - theta
T = np.sin(T)
A = K*T
A = A.dot(np.ones(1000))
return A
rng = np.random.default_rng()
theta = rng.uniform(-np.pi,np.pi,1000)
for K in (rng.uniform(-10,10,shp) for shp in ((),(1000,),(1000,1000))):
print(np.allclose(orig(),pp()))
print(timeit(orig,number=10))
print(timeit(pp,number=10))
Пример выполнения:
True
0.493153132032603
0.0012746050488203764
True
0.49636399815790355
0.0012419759295880795
True
0.47554834792390466
0.05685037490911782
Таким образом, с K
скаляром или вектором мы получаем ускорение ~ 350x, в то время как K
является матрицей, это всего ~ 8x.