Мне нужно одновременно заполнить два взаимозависимых массива на основе их предыдущего элемента, например:

import numpy as np
a = np.zeros(100)
b = np.zeros(100)
c = np.random.random(100)

for num in range(1, len(a)):
    a[num] = b[num-1] + c[num]
    b[num] = b[num-1] + a[num]

Есть ли способ действительно векторизовать это (т.е. не использовать numpy.vectorize) с помощью numpy? Обратите внимание, что это произвольные массивы, не ищущие решения для этих конкретных значений.

2
gucciolo 8 Сен 2016 в 16:42

3 ответа

Лучший ответ

Как упоминалось в сообщении @ Praveen, мы можем написать эти выражения для нескольких итераций, пытаясь найти замкнутую форму, и это будет, конечно, треугольная матрица для c. Затем нам просто нужно добавить итеративно масштабируемый b[0], чтобы получить полный b. Чтобы получить a, мы просто добавляем сдвинутые версии b и c.

Итак, с точки зрения реализации вот другой подход к этому, используя некоторые {{X0} } и dot-product в целях повышения эффективности -

p = 2**np.arange(a.size-1)
scale1 = p[:,None]//p
b_out = np.append(b[0],scale1.dot(c[1:]) + 2*p*b[0])
a_out = np.append(a[0],b_out[:-1] + c[1:])

Если a и b должны всегда начинаться как 0, код для последних двух шагов упростится до -

b_out = np.append(0,scale1.dot(c[1:]))
a_out = np.append(0,b_out[:-1] + c[1:])
3
Divakar 8 Сен 2016 в 15:05

Да, есть:

c = np.arange(100)
a = 2 ** c - 1
b = numpy.cumsum(a)
2
Nils Werner 8 Сен 2016 в 13:54

Ясно, что обновления:

a_i = b_i-1 + c_i
b_i = 2*b_i-1 + c_i

Записывая рекурсию,

b_0 = c_0              # I'm not sure if c_0 is to be used
b_1 = 2*b_0 + c_1
    = 2*c_0 + c_1
b_2 = 2*b_1 + c_2
    = 2*(2*c_0 + c_1) + c_2
    = 4*c_0 + 2*c_1 + c_2
b_3 = 2*b_2 + c_3
    = 2*(4*c_0 + 2*c_1 + c_2) + c_3
    = 8*c_0 + 4*c_1 + 2*c_2 + c_3

Так казалось бы

b_i = np.sum((2**np.arange(i+1))[::-1] * c[:i])
a_i = b_i-1 + c_i

Здесь невозможно сделать кумулятивную сумму, потому что коэффициент при c_i постоянно меняется.

Самый простой способ полностью векторизовать это, вероятно, просто использовать гигантскую матрицу. Если c имеет размер N:

t = np.zeros((N, N))
x, y = np.tril_indices(N)
t[x, y] = 2 ** (x - y)

Это дает нам:

>>> t
array([[ 1.,  0.,  0.,  0.],
       [ 2.,  1.,  0.,  0.],
       [ 4.,  2.,  1.,  0.],
       [ 8.,  4.,  2.,  1.]])

Итак, теперь вы можете:

b = np.sum(t * c, axis=1)
a = np.zeros(N)
a[1:] = b[:-1] + c[1:]

Я бы, наверное, не рекомендовал это решение. Судя по тому немногому, что я знаю о вычислительных методах, это не кажется численно стабильным для больших N. Но у меня такое чувство, что это будет верно для любого векторизованного решения, которое выполняет суммирование в конце. Может быть, вам стоит попробовать как цикл for, так и этот фрагмент кода и посмотреть, продолжают ли ваши ошибки расти с векторизованным решением.

1
Praveen 8 Сен 2016 в 14:58