Вот начало. Во-первых, мои извинения за любые ошибки.
Я экспериментировал с парой разных подходов. Меня немного смутили ограничения суммирования - должен ли верхний предел быть , а не i - 1 ?яя - 1
Изменить: нет, верхний предел был правильным, как указано в вопросе. Я оставил все как есть, потому что другой ответ теперь использует тот же код, но исправить это просто.
Сначала зацикленная версия:
def looped_ver(k, a):
x = np.empty_like(a)
for i in range(x.size):
sm = 0
for j in range(0, i+1):
sm += k[i-j,j] * a[i-j] * a[j]
x[i] = sm
return x
Я сделал это один цикл с кусочками:
def vectorized_ver(k, a):
ktr = zeros_like(k)
ar = zeros_like(k)
sz = len(a)
for i in range(sz):
ktr[i,:i+1] = k[::-1].diagonal(-sz+i+1)
a_ = a[:i+1]
ar[i,:i+1] = a_[::-1] * a_
return np.sum(ktr * ar, 1)
Цифровая версия с одним явным циклом примерно в 25 раз быстрее на моем компьютере, когда .n = 5000
Затем я написал версию (более читабельного) зацикленного кода на Cython.
import numpy as np
import cython
cimport numpy as np
@cython.boundscheck(False)
@cython.wraparound(False)
def cyth_ver(double [:, ::1] k not None,
double [:] a not None):
cdef double[:] x = np.empty_like(a)
cdef double sm
cdef int i, j
for i in range(len(a)):
sm = 0.0
for j in range(i+1):
sm = sm + k[i-j,j] * a[i-j] * a[j]
x[i] = sm
return x
На моем ноутбуке этот примерно в 200 раз быстрее, чем зацикленная версия (и в 8 раз быстрее, чем одноконтурная векторизованная версия). Я уверен, что другие могут сделать лучше.
Я играл с версией Julia, и она казалась (если я правильно рассчитал время) сопоставимой с кодом Cython.