是的,您可以通过在产品中间进行统一转换来做到这一点,因此我们得到 p>
A = V * U * V^-1 = V * T' * T * U * T' * T * V^{-1}。
一旦你有了这个想法,你可以通过平铺来优化代码,但让我们通过显式形成 T 来以天真的方式来做。
如果矩阵是偶数大小,则所有块都是复共轭。否则我们得到一个零作为特征值。保证特征值的实部为零,因此首先要清除噪声,然后进行排序,使零在左上角(任意选择)。
n = 5
a = np.random.rand(n,n)
a=a-np.transpose(a)
[u,v] = np.linalg.eig(a)
perm = np.argsort(np.abs(np.imag(u)))
unew = 1j*np.imag(u[perm])
显然,我们也需要对特征向量矩阵进行重新排序以保持等价。
vnew = v[:,perm]
到目前为止,除了在特征值分解中重新排序中间特征值矩阵外,我们什么也没做。现在我们从复杂形式切换到真正的块对角形式。
首先我们要知道有多少个零特征值
numblocks = np.flatnonzero(unew).size // 2
num_zeros = n - (2 * numblocks)
然后我们基本上,形成另一个酉变换(这次是复杂的)并以同样的方式粘贴它
T = sp.linalg.block_diag(*[1.]*num_zeros,np.kron(1/np.sqrt(2)*np.eye(numblocks),np.array([[1.,1j],[1,-1j]])))
Eigs = np.real(T.conj().T.dot(np.diag(unew).dot(T)))
Evecs = np.real(vnew.dot(T))
这为您提供了新的实值分解。所以代码都在一个地方
n = 5
a = np.random.rand(n,n)
a=a-np.transpose(a)
[u,v] = np.linalg.eig(a)
perm = np.argsort(np.abs(np.imag(u)))
unew = 1j*np.imag(u[perm])
vnew = v[perm,:]
numblocks = np.flatnonzero(unew).size // 2
num_zeros = n - (2 * numblocks)
T = sp.linalg.block_diag(*[1.]*num_zeros,np.kron(1/np.sqrt(2)*np.eye(numblocks),np.array([[1.,1j],[1,-1j]])))
Eigs = np.real(T.conj().T.dot(np.diag(unew).dot(T)))
Evecs = np.real(vnew.dot(T))
print(np.allclose(Evecs.dot(Eigs.dot(np.linalg.inv(Evecs))) - a,np.zeros((n,n))))
给出True。请注意,这是获得真实谱分解的朴素方式。有很多地方需要跟踪数值误差累积。
示例输出
Eigs
Out[379]:
array([[ 0. , 0. , 0. , 0. , 0. ],
[ 0. , 0. , -0.61882847, 0. , 0. ],
[ 0. , 0.61882847, 0. , 0. , 0. ],
[ 0. , 0. , 0. , 0. , -1.05097581],
[ 0. , 0. , 0. , 1.05097581, 0. ]])
Evecs
Out[380]:
array([[-0.15419078, -0.27710323, -0.39594838, 0.05427001, -0.51566173],
[-0.22985364, 0.0834649 , 0.23147553, -0.085043 , -0.74279915],
[ 0.63465436, 0.49265672, 0. , 0.20226271, -0.38686576],
[-0.02610706, 0.60684296, -0.17832525, 0.23822511, 0.18076858],
[-0.14115513, -0.23511356, 0.08856671, 0.94454277, 0. ]])