提问人:Silviu 提问时间:11/9/2023 更新时间:11/18/2023 访问量:176
在 Python 中对角化矩阵时保持对角线元素的顺序
Keep order of diagonal elements when diagonalizing a matrix in Python
问:
我有一个非对角线矩阵,但非对角线元素比对角线元素小得多,因此在对角线化时,我预计对角线元素会发生变化,但只是一点点。我目前只是用来获取特征值,但是我希望这些值的顺序与对角化前的(相似)值相同。因此,如果原始矩阵中的对角线是 (100,200,300),那么在对角化后,我希望输出 (101,202,303) 而不是 (202,303,101)。我需要这个来跟踪对角化后哪个值映射到哪个值。我该怎么做?似乎对 ?谢谢!numpy.linalg.eig
numpy.linalg.eig
答:
用于计算矩阵的特征值时,返回的特征值不一定以任何特定方式排序。您可以参考 numpy 手册 https://numpy.org/doc/stable/reference/generated/numpy.linalg.eig.htmlnumpy.linalg.eig
现在,为了解决这个问题,我们可以举一个示例矩阵,其中非对角线元素不为零但很小,然后尝试将它们映射到它们的原始顺序
example_matrix = np.array([[100, 0.2, 0.3], [0.2, 200, 0.5], [0.3, 0.5, 300]])
# Compute eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(example_matrix)
# Original diagonal elements
original_diagonal = np.diag(example_matrix)
# Sort eigenvalues by how close they are to the original diagonal elements
sorted_indices = np.argsort(np.abs(eigenvalues - original_diagonal))
# Sorted eigenvalues and eigenvectors
sorted_eigenvalues = eigenvalues[sorted_indices]
sorted_eigenvectors = eigenvectors[:, sorted_indices]
# Output the sorted eigenvalues and their corresponding eigenvectors
sorted_eigenvalues, sorted_eigenvectors
结果
(array([ 99.99915299, 199.99789408, 300.00295293]),
array([[-0.9999969 , -0.001985 , 0.00150496],
[ 0.0019925 , -0.9999855 , 0.00500279],
[ 0.00149501, 0.00500578, 0.99998635]]))
基本上,您需要根据与原始对角线的接近程度对特征值及其相应的特征向量进行排序。这样,您可以跟踪对特征值进行排序的排列,以便将它们映射回其原始顺序。
评论
框架挑战
我不认为这个问题可以有严格的解决方案,肯定不会使用直接的numpy。
通过对源代码的粗略检查,我认为要计算特征值 numpy 首先要计算参数矩阵行列式的根的神圣方法。这是最快、最准确的方法,可以保证有效。
然而,它不能保持特征值顺序。
为简单起见,请考虑一个 2 x 2 矩阵:
[[ 10 .1 ] [.1 20]]
这有 lambda-det 的L^2-30L+199.99
但是,如果您考虑另一个矩阵,通过交换前一个对角线获得的相反顺序的相同值......
[[ 20 .1 ] [.1 10]]
...你最终会得到相同的决定因素。因此,从这个行列式中,你无法判断你从哪个矩阵开始,因此你无法确定原始对角线元素的顺序。继续进行计算,从这里开始,该信息将不再存在。您确实获得了特征值,但没有得到它们的原始顺序。
当你构建行列式时,就像你乘以 x*y 一样,关于因子顺序的信息会丢失(在乘法的情况下,它总是会完全丢失)。
因此,特征值似乎相对于原始对角线进行了“洗牌”。
但实际发生的并不是原始值“略有偏移”;每次它们都完全丢失了,其他类似的值会在它们的位置上重新创建。它们“看起来像”以前的值这一事实是一条红鲱鱼。因此,不能保证比赛是可能的。
这有点像泰迪和狮子等“消失的幻觉”谜题的数学版本:
您可以尝试做的是匹配“后验”值,即选择最接近原始对角线的特征值排列。
有一些算法可以做到这一点,使用不同的指标来确定哪种排列“更接近”(通常是最小二乘法)。
一种快速方法是按升序对初始对角线值进行排序,记录使用的排列(这是 O(n log n)),然后按升序对特征值进行排序并反向应用排列。这会让你得到一个“势均力敌”。
但是,仍然可以使用不同的算法(例如 Jacobi 方法或其他迭代方法)执行类似于您描述的内容。
请注意,此类算法需要在本地保留顺序。一个地方的价值仍然存在。但它仍然可能会发生变化,以便您以与预期不同的顺序获得值,例如,您从对角线 [2.1, 1.9, 12] 开始,第一个元素缩小到 1.999,第二个元素增长到 2.001,您可能会相信“两个元素被交换了”。
在Interwebs周围发现的实现
正如我所说,我的线性代数时代已经过去很久了,但我记得雅可比方法是迭代的,所以可能适合你的要求。我寻找它并停在第一个结果上。在我看来是正确的。如果不是这样,更集中的搜索可能仍然会产生一个有效的 Python 实现;该理论已得到证实。
a = array([[2.0,0.1,0.1],[0.1,7.0,0.1],[0.1, 0.1, 11.0]])
lam,x = jacobi(a,tol = 1.0e-9)
print(lam)
# Expected value: close to [2 7 11] in this order
[1.99693421 6.99940092 11.00366487]
运行其他几个随机值产生了一致的结果。
评论
[M | I ] -> [I | D ]
import numpy as np
# Your non-diagonal matrix
A = np.array([[10, 0.1, 0.2],[0.3, 20, 0.4], [0.5, 0.6, 30]])
# Compute eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(A)
# Sort eigenvalues and corresponding eigenvectors based on magnitudes
sorted_indices = np.argsort(np.abs(eigenvalues))
sorted_eigenvalues = eigenvalues[sorted_indices]
sorted_eigenvectors = eigenvectors[:, sorted_indices]
# Print the sorted eigenvalues and corresponding eigenvectors
print("Original Eigenvalues:", eigenvalues)
print("Sorted Eigenvalues:", sorted_eigenvalues)
print("Corresponding Eigenvectors:", sorted_eigenvectors)
现在,您可以根据需要使用 sorted_eigenvalues 和 sorted_eigenvectors。
基于特征向量的方法难以处理紧密值的对角线元素,影响了其后对角线化的精度。
制作了一个简洁的小方法,称为迭代调整方法。这种方法会一点一点地调整矩阵,以按照您想要的方式排列这些特征值。
试试这个:
import numpy as np
def iterative_adjustment(matrix, max_iterations=100, tolerance=1e-4):
original_diagonal = np.diag(matrix)
for _ in range(max_iterations):
eigenvalues, eigenvectors = np.linalg.eig(matrix)
sorted_indices = np.argsort(eigenvalues)
eigenvalues = eigenvalues[sorted_indices]
eigenvectors = eigenvectors[:, sorted_indices]
if np.allclose(original_diagonal, eigenvalues, atol=tolerance):
return eigenvalues, eigenvectors
adjustment = np.diag(eigenvalues - original_diagonal)
matrix -= adjustment * 0.01
return eigenvalues, eigenvectors
用法:只需将你的矩阵插入这个函数,它就会为你带来魔力。这就像在这里和那里给你的矩阵一点点推动,直到一切都到位。
它是如何工作的?
初始对角化:首先,我们进行标准对角化以获得起点 - 矩阵的特征值和特征向量。
迭代调整:
我们看看我们得到的特征值如何与原始对角线数字匹配。 然后,我们对您的矩阵进行小幅调整,使这些特征值更接近您的原始数字。 我们继续做这个小舞蹈 - 调整和对角线化 - 直到特征值与原始对角线数字的顺序对齐。
处理收盘数字:
这种方法非常漂亮,因为它还可以处理您的对角线数字非常接近的情况。它轻轻地推动特征值,使它们保持正确的顺序。
评论
让我们成为对角线元素的列表。对列表进行排序将使我们能够找到以下排列:diag
sorted
sorted_diag
diag
sorted_diag = sorted(diag)
perm = [sorted_diag.index(d) for d in diag]
perm
会确实是排列,因为会回来.[sorted_diag[i] for i in perm]
diag
现在,由于特征值集与对角线元素集“相似”,因此在排序的特征值数组上使用此排列应按我们想要的顺序生成特征值。如果是特征值的数组,那么我们可以eigens
sorted_eigens = sorted(eigens)
eigens_good = [sorted_eigens[i] for i in perm]
所以整个事情是这样的:
mat = np.array([[203, 0.4, 0.6], [0.3, 301, 0.7], [0.6, 0.1, 101]])
diag = np.diag(mat) # [203, 301, 101]
eigens, _ = np.linalg.ein(mat) # [203.00228619, 100.99612982, 301.00158399]
sorted_diag = sorted(diag) # [101, 203, 301]
perm = [sorted_diag.index(d) for d in diag] # [1, 2, 0]
sorted_eigens = sorted(eigens) # [100.99612982, 203.00228619, 301.00158399]
eigens_good = [sorted_eigens[i] for i in perm] # [203.00228619, 100.99612982, 301.00158399]
这正是我们想要的,就像最初的订单一样!eigens_good
[203, 101, 301]
注意:可以通过使用与列表推导式不同的东西来对其进行更多优化,但我认为这可能已经足够了。sorted
对角线中的相同值:
如果对角线具有相同的元素,则上述方法不会立即起作用,但您可以获得类似的东西。您只需要更仔细地生成,例如,通过“弹出”排序数组副本的元素。perm
diag = np.diag(mat)
sorted_diag = sorted(diag)
perm = [None] * len(diag)
temp = sorted_diag.copy()
for i, d in enumerate(diag):
index_d = temp.index(d)
perm[i] = index_d
temp[index_d] = None
with ,正确地再次产生,因此以与之前相同的方式使用 with 应该没问题。diag = [203, 101, 404, 101]
[sorted_diag[i] for i in perm]
diag
perm
eigens
评论