Мне нужно было вычислить Q ^ N для множества различных значений N (от 1 до 10000), а Numpy был слишком медленным.
И что касается моей первоначальной проблемы, второй метод позволяет мне вычислить P, diag and P1
только один раз и использовать его тысячи раз. Это в 8 раз быстрее, используя этот метод.
Ответ 1
Я частично отвечаю на свой вопрос:
В соответствии с исходным кодом, я думаю, что Numpy использует Exponentiation by Squaring:
# binary decomposition to reduce the number of Matrix
# multiplications for n > 3.
beta = binary_repr(n)
Z, q, t = M, 0, len(beta)
while beta[t-q-1] == '0':
Z = N.dot(Z, Z)
q += 1
result = Z
for k in range(q+1, t):
Z = N.dot(Z, Z)
if beta[t-k-1] == '1':
result = N.dot(result, Z)
return result
Что в моем случае медленнее, когда n
велико, чем вычисление собственных значений и собственных векторов и вычисление M ^ N как равное P D ^ N P ^ -1.
Теперь, касаясь моих вопросов:
В этом случае невозможно использовать этот последний метод для вычисления Q ^ N?
Когда некоторые собственные значения равны, невозможно будет инвертировать P. кто-то предложил сделать это в Numpy в отслеживании проблем. Ответ был: "Ваш подход действителен только для не дефектных плотных матриц".
Можно ли использовать его в моем случае (матрица Q, как указано здесь)?
Не всегда, у меня может быть несколько одинаковых собственных значений.
Есть ли в numpy функция, которая уже делает это?
Я думаю, что это в SciPy: https://github.com/scipy/scipy/blob/v0.12.0/scipy/linalg/matfuncs.py#L57
Таким образом, мы также можем сделать это:
LA.expm(n*LA.logm(m))
для вычисления m ^ n.
Как я могу изменить матрицу P, чтобы она стала обратимой, но результирующая матрица не слишком изменена? Я имею в виду, это нормально, если значения близки к реальному результату, по закрытию я имею в виду ~ 0,0001.
Я не могу просто добавить значение epsilon, потому что метод декомпозиции разумен, когда значения слишком близки. Я уверен, что это может привести к непредсказуемым ошибкам.
Ответ 2
Вы уже поняли, что ваши собственные значения будут (0, a, b, c, ..., 1)
. Позвольте мне переименовать ваши параметры, так что собственные значения (0, e1, e2, e3, ..., 1)
. Чтобы узнать собственный вектор (v0, v1, v2, ..., v(n-1))
, соответствующий собственному значению ej
, вам необходимо решить систему уравнений:
v1 = v0*ej
v1*e1 + v2*(1-e1) = v1*ej
v2*e2 + v3*(1-e2) = v2*ej
...
vj*ej + v(j+1)*(1-ej) = vj*ej
...
v(n-1) = v(n-1)*ej
Более или менее понятно, что если все ваши ei
различны, а ни один не равен 0
или 1
, тогда решение всегда определено всегда, а при работе с ej
результат собственный вектор имеет первые компоненты j
, отличные от нуля, а остальные равны нулю. Это гарантирует, что ни один собственный вектор не является линейной комбинацией остальных, и, следовательно, матрица собственного вектора обратим.
Проблема возникает, когда некоторые из ваших ei
либо 0
, либо 1
, либо повторяются. Я не смог придумать доказательство этого, но, экспериментируя со следующим кодом, кажется, что вы должны только беспокоиться, если любые два из ваших ei
равны и отличаются от 1
:
>>> def make_mat(values):
... n = len(values) + 2
... main_diag = np.concatenate(([0], values, [1]))
... up_diag = 1 - np.concatenate(([0], values))
... return np.diag(main_diag) + np.diag(up_diag, k=1)
>>> make_mat([4,5,6])
array([[ 0, 1, 0, 0, 0],
[ 0, 4, -3, 0, 0],
[ 0, 0, 5, -4, 0],
[ 0, 0, 0, 6, -5],
[ 0, 0, 0, 0, 1]])
>>> a, b = np.linalg.eig(make_mat([4,5,6]))
>>> a
array([ 0., 4., 5., 6., 1.])
>>> b
array([[ 1. , 0.24253563, -0.18641093, 0.13608276, 0.4472136 ],
[ 0. , 0.9701425 , -0.93205465, 0.81649658, 0.4472136 ],
[ 0. , 0. , 0.31068488, -0.54433105, 0.4472136 ],
[ 0. , 0. , 0. , 0.13608276, 0.4472136 ],
[ 0. , 0. , 0. , 0. , 0.4472136 ]])
И теперь для некоторых тестовых случаев:
>>> a, b = np.linalg.eig(make_mat([1,0,3])) # having a 0 or 1 is OK
>>> b
array([[ 1. , 0.70710678, 0. , 0. , 0. ],
[ 0. , 0.70710678, 0. , 0. , 0. ],
[ 0. , 0. , 1. , 0.31622777, 0.57735027],
[ 0. , 0. , 0. , 0.9486833 , 0.57735027],
[ 0. , 0. , 0. , 0. , 0.57735027]])
>>> a, b = np.linalg.eig(make_mat([1,1,3])) # repeating 1 is OK
>>> b
array([[ 1. , 0.70710678, 0. , 0. , 0. ],
[ 0. , 0.70710678, 0. , 0. , 0. ],
[ 0. , 0. , 1. , 0. , 0. ],
[ 0. , 0. , 0. , 1. , 0.70710678],
[ 0. , 0. , 0. , 0. , 0.70710678]])
>>> a, b = np.linalg.eig(make_mat([0,0,3])) # repeating 0 is not OK
>>> np.round(b, 3)
array([[ 1. , -1. , 1. , 0.035, 0.447],
[ 0. , 0. , 0. , 0.105, 0.447],
[ 0. , 0. , 0. , 0.314, 0.447],
[ 0. , 0. , 0. , 0.943, 0.447],
[ 0. , 0. , 0. , 0. , 0.447]])
>>> a, b = np.linalg.eig(make_mat([2,3,3])) # repeating other values are not OK
>>> np.round(b, 3)
array([[ 1. , 0.447, -0.229, -0.229, 0.447],
[ 0. , 0.894, -0.688, -0.688, 0.447],
[ 0. , 0. , 0.688, 0.688, 0.447],
[ 0. , 0. , 0. , 0. , 0.447],
[ 0. , 0. , 0. , 0. , 0.447]])