为何NumPy与SymPy计算的特征向量存在差异?
我正在开展一个涉及特征值(eigenvalues)与特征向量(eigenvectors)计算的项目。最初全程使用NumPy开发,但因多种原因不得不切换为SymPy。核心问题是,即使计算同一矩阵的特征向量,两者结果也不匹配。
NumPy实现与输出
原始NumPy代码:
import numpy as np from numpy.linalg import inv, eig T_num = np.array([[-0.873242282149309, 0.858808525419568, 0.00866025403784438, 0.00866025403784438], [-0.858808525419568, 0.873242282149309, -0.00866025403784438, -0.00866025403784438], [0.0360843918243516, 0.0360843918243516, -0.909326673973660, 0.822724133595217], [-0.0360843918243516, -0.0360843918243516, -0.822724133595217, 0.909326673973660]]) eigenvalues, eigenvectors = np.linalg.eig(T_num) vr = np.diag(eigenvalues) Kr = eigenvectors
输出的特征向量矩阵Kr:
[[-0.16735621+0.00000000e+00j -0.54321448+5.43103641e-09j -0.54321448-5.43103641e-09j -0.10224205+0.00000000e+00j] [-0.10224205+0.00000000e+00j -0.54321448+0.00000000e+00j -0.54321448-0.00000000e+00j -0.16735621+0.00000000e+00j] [ 0.83678107+0.00000000e+00j -0.45267873+4.52586368e-09j -0.45267873-4.52586368e-09j 0.51121024+0.00000000e+00j] [ 0.51121024+0.00000000e+00j -0.45267873+4.96308368e-24j -0.45267873-4.96308368e-24j 0.83678107+0.00000000e+00j]]
SymPy实现与输出
我知晓NumPy的特征向量已归一化,SymPy的未归一化,因此使用.normalized()方法对SymPy结果进行归一化,代码如下:
import sympy as sp T_sym = sp.Matrix([[-0.873242282149309, 0.858808525419568, 0.00866025403784438, 0.00866025403784438], [-0.858808525419568, 0.873242282149309, -0.00866025403784438, -0.00866025403784438], [0.0360843918243516, 0.0360843918243516, -0.909326673973660, 0.822724133595217], [-0.0360843918243516, -0.0360843918243516, -0.822724133595217, 0.909326673973660]]) vr_sym = list(T_sym.eigenvals().keys()) eigenvectores = T_sym.eigenvects() lista_vectp = [] for _, _, eigenvectors in eigenvectores: for vector in eigenvectors: normalized_vector = vector.normalized() lista_vectp.append(normalized_vector.T) Kr_sym = sp.Matrix(lista_vectp).T
输出结果:
[[-0.166635697352398 - 0.0155127875107584*I, -0.259982390116971 - 0.476960289513235*I, -0.214652107972921 + 0.499005456909564*I, 0.102242047382989 - 2.20934780343373e-64*I], [-0.101801866238621 - 0.00947714531910078*I, -0.259982395640837 - 0.476960299647248*I, -0.214652103412191 + 0.499005446307156*I, 0.167356213534108 - 2.62764866783283e-64*I], [0.83317848676198 + 0.077563937553791*I, -0.216651991764145 - 0.397466907927701*I, -0.178876756644103 + 0.415837880757976*I, -0.51121023691494 - 8.33146467644485e-65*I], [0.509009331193098 + 0.0473857265955033*I, -0.216651996367367 - 0.397466916372712*I, -0.178876752843495 + 0.415837871922635*I, -0.83678106767053 - 1.92452791847659e-64*I]]
可见SymPy计算出的特征向量虚部远大于NumPy的结果,请问这一差异产生的原因是什么?
原因分析
1. 数值计算与符号计算的核心差异
NumPy是数值计算库,基于浮点数近似求解特征值问题,采用的算法(如QR迭代)会自动将极小的虚部判定为浮点噪声,输出时直接显示为接近0的数值(比如你看到的1e-9、1e-24级别虚部)。而SymPy是符号计算库,会严格按照代数规则求解特征方程,哪怕输入矩阵存在微小的数值偏差(比如你输入的矩阵元素是截断后的近似小数,导致矩阵并非严格实对称),也会保留所有精确计算出的结果,包括这些偏差带来的虚部。
2. 特征向量的非唯一性
特征向量本身不唯一:对于同一特征值,其特征向量乘以任意非零标量(包括复数标量)后,仍然是该特征值的特征向量。NumPy输出的是实部主导、虚部可忽略的向量,而SymPy可能因求解路径不同,选择了不同的复数缩放形式,导致看起来差异较大,但本质上和NumPy的向量是等价的——你可以尝试将SymPy的特征向量乘以某个复数因子,或者取共轭,就能和NumPy的结果对齐。
3. 归一化的细节差异
虽然两者都做了L2归一化,但实现逻辑有区别:NumPy在归一化时会自动忽略极小的虚部(当作数值误差),而SymPy会严格保留所有计算出的虚部,哪怕这些虚部是由输入矩阵的微小非对称性导致的。
验证建议
- 检查原始矩阵是否严格实对称:如果原始矩阵应该是对称的,输入时的小数截断可能导致了非对称,这是虚部产生的根源。
- 对SymPy的结果取实部(或忽略极小虚部),再与NumPy结果对比,两者会高度接近。
- 验证特征向量有效性:分别计算
T @ v - λ*v(NumPy用数值精度,SymPy用符号精度),结果都应该接近零向量。
内容的提问来源于stack exchange,提问作者Ectobius Rex

