Подход № 1
Для векторизованного с NumPy вы можете использовать маскирующий после получения всех попарных умножений с внешним умножением, например -
def pairwise_multiply_masking(a):
return (a[:,None]*a)[~np.tri(len(a),k=-1,dtype=bool)]
Подход № 2
Для действительно больших входных 1D-массивов мы можем захотеть прибегнуть к итеративному методу slicing
, который использует one-l oop -
def pairwise_multiply_iterative_slicing(a):
n = len(a)
N = (n*(n+1))//2
out = np.empty(N, dtype=a.dtype)
c = np.r_[0,np.arange(n,0,-1)].cumsum()
for ii,(i,j) in enumerate(zip(c[:-1],c[1:])):
out[i:j] = a[ii:]*a[ii]
return out
Сравнительный анализ
Мы включим pairwise_products
и pairwise_products_numba
из решения @ orlp в настройку.
Использование пакета benchit
(несколько инструментов тестирования собраны вместе; отказ от ответственности: я являюсь его автором) для тестирования предлагаемых решений.
import benchit
funcs = [pairwise_multiply_masking, pairwise_multiply_iterative_slicing, pairwise_products_numba, pairwise_products]
in_ = [np.random.rand(n) for n in [10,50,100,200,500,1000,5000]]
t = benchit.timings(funcs, in_)
t.plot(logx=True, save='timings.png')
t.speedups(-1).plot(logx=True, logy=False, save='speedups.png')
Результаты (тайминги и ускорение более pairwise_products
) -


Как видно из графиков тенденций, для действительно больших массивов вариант, основанный на нарезке, начнет выигрывать, в противном случае вариант с векторизацией будет работать хорошо.
Предложения
- Мы также можем изучить * 10 52 * для более эффективного выполнения внешних умножений для больших массивов.