1.2.10 计算逆矩阵
主线比喻:逆矩阵 = 矩阵运算里的 Ctrl+Z 撤销键
① 导入:先变形,再撤销
修图时把照片旋转、拉伸,想恢复原样怎么办?按Ctrl+Z一键撤销。
矩阵 A 对数据做一次「加工」,A 的逆矩阵就是把这次加工「还原」回去。
② 什么是逆矩阵
- 矩阵= 一台「加工机器」:把数据变个样子
- 逆矩阵= 这台机器的「还原按钮」:把加工过的数据变回原样
- 单位阵 I= 一台「啥也不干」的机器:进去什么样,出来还是什么样
换个比喻:把一句话翻译成英语(乘 A),再翻译回中文(乘 A 的逆),就得到原话。
数学上就是:A · A⁻¹ = I(先加工再还原 = 啥也没变)
③ 怎么知道能不能还原?
算一个数值叫行列式 det,它就是一张「体检报告」:
- det ≠ 0 → 信息没丢,能还原
- det = 0 → 信息被「压扁」了,还原不回去
想象复印机把一张画压成一条线:原来丰富的图案信息丢了,再也变不回原图。det=0 的矩阵就是这样。
2×2 手算口诀(方便对答案):主对角线交换位置、副对角线变号、整体除以 det。
🤔 互动预测 1
A @ A_inv会精确等于单位阵 I 吗?先想 30 秒,再运行下面这格。
importnumpyasnpimportmatplotlib.pyplotasplt plt.rcParams['font.sans-serif']=['Microsoft YaHei','SimHei']plt.rcParams['axes.unicode_minus']=False# 例 1:能还原的矩阵——求逆并验证A=np.array([[2,1],[1,2]])A_inv=np.linalg.inv(A)print('A =\n',A)print('A 的逆矩阵(还原矩阵)=\n',A_inv)print('\n验证:A @ A_inv =\n',A @ A_inv)print('用 allclose 判断(容忍小数尾巴):',np.allclose(A @ A_inv,np.eye(2)))# 手算口诀核对a,b,c,d=2,1,1,2manual=np.array([[d,-b],[-c,a]])/(a*d-b*c)print('\n手算口诀结果与 inv 一致:',np.allclose(manual,A_inv))运行结果:
A = [[2 1] [1 2]] A 的逆矩阵(还原矩阵)= [[ 0.66666667 -0.33333333] [-0.33333333 0.66666667]] 验证:A @ A_inv = [[1. 0.] [0. 1.]] 用 allclose 判断(容忍小数尾巴): True 手算口诀结果与 inv 一致: True💡 浮点误差(小数尾巴)
A @ A_inv对角线上是 1,但别的位置有1e-16这种极小量,不是精确的 0——这是计算机存小数时的「四舍五入尾巴」。
所以判断「是不是单位阵」不能用==,要用np.allclose()。
④ 先找找感觉:对角矩阵的逆 = 每个数取倒数
运行下面这格,你会看到结果特别好记。
# 例 2:对角矩阵——逆矩阵就是“每个数取倒数”D=np.array([[2,0],[0,4]])D_inv=np.linalg.inv(D)print('D =\n',D)print('D 的逆矩阵(对角线取倒数)=\n',D_inv)运行结果:
D = [[2 0] [0 4]] D 的逆矩阵(对角线取倒数)= [[0.5 0. ] [0. 0.25]]2 的倒数是 0.5,4 的倒数是 0.25。对角矩阵求逆,就是把对角线上每个数取倒数——这就是「还原」最直观的感觉。
🤔 互动预测 2
矩阵 B =[[1,2],[2,4]],第二行是第一行的 2 倍。
它还能「还原」吗?先预测,再运行下一格揭晓。
# 例 3:还原不回去的矩阵——det 等于 0B=np.array([[1,2],[2,4]])# 第二行是第一行的 2 倍,信息被“压扁”print('B =\n',B)print('det(B) =',np.linalg.det(B))# 0.0try:np.linalg.inv(B)exceptnp.linalg.LinAlgErrorase:print('强行求逆报错:',e)运行结果:
B = [[1 2] [2 4]] det(B) = 0.0 强行求逆报错: Singular matrixdet=0 说明这台「加工机器」把信息压扁了,还原按钮自然失效,Python 会直接报错提醒你。
⑤ 还原不回去时:求一个「尽量还原」
现实数据经常有冗余(比如两列几乎一样),严格还原做不到。这时退而求其次,求一个广义逆(伪逆):np.linalg.pinv(),奇异矩阵也能算。
类比:回不到一模一样,就给出「最接近」的答案。
# 例 4:尽量还原(广义逆 / 伪逆)B_pinv=np.linalg.pinv(B)print('B 的广义逆 =\n',B_pinv)print('验证 B @ B_pinv @ B ≈ B:',np.allclose(B @ B_pinv @ B,B))运行结果:
B 的广义逆 = [[0.04 0.08] [0.08 0.16]] 验证 B @ B_pinv @ B ≈ B: True⑥ 为什么要学逆矩阵?——解方程组
方程组x + 2y = 5、2x + y = 4可以写成矩阵形式C·x = b,两边同乘 C 的逆:x = C⁻¹·b。
# 例 5:用逆矩阵解方程组C=np.array([[1,2],[2,1]])rhs=np.array([5,4])x=np.linalg.inv(C)@ rhsprint('x + 2y = 5');print('2x + y = 4')print('解得 x =',x)# [1. 2.],即 x=1, y=2print('与 np.linalg.solve 一致:',np.allclose(x,np.linalg.solve(C,rhs)))运行结果:
x + 2y = 5 2x + y = 4 解得 x = [1. 2.] 与 np.linalg.solve 一致: True⑦ 画图看:能还原 vs 不能还原
先看能还原的情况:单位圆被 A 拉成椭圆,再用 A 的逆把它拉回原状。
theta=np.linspace(0,2*np.pi,300)circle=np.vstack([np.cos(theta),np.sin(theta)])img_A=A @ circle back=A_inv @ img_A fig,ax=plt.subplots(1,3,figsize=(12,4),sharex=True,sharey=True)forainax:a.axhline(0,color='gray',lw=0.5)a.axvline(0,color='gray',lw=0.5)a.set_aspect('equal')ax[0].plot(*circle,lw=2);ax[0].set_title('原始单位圆')ax[1].plot(*img_A,lw=2);ax[1].set_title('A 变换后(椭圆)')ax[2].plot(*back,lw=2);ax[2].set_title('再用 A 的逆拉回')fig.suptitle('能还原:A 的逆把 A 的变换“撤销”回原状')plt.show()再看不能还原的情况:同样一个圆,被 B 直接压成一条线——信息丢了,再也变不回圆。
fig2,ax2=plt.subplots(1,2,figsize=(8,4),sharex=True,sharey=True)forainax2:a.axhline(0,color='gray',lw=0.5)a.axvline(0,color='gray',lw=0.5)a.set_aspect('equal')ax2[0].plot(*circle,lw=2);ax2[0].set_title('原始单位圆')img_B=B @ circle ax2[1].plot(*img_B,lw=2);ax2[1].set_title('B 变换后:被压成一条线')fig2.suptitle('不能还原:信息被“压扁”,回不去了')plt.show()⑧ 小结
inv是完美还原,det是能不能还原的体检报告,pinv是还原不了时的尽量还原。
| 函数 | 作用(大白话) | 什么时候用 |
|---|---|---|
np.linalg.inv | 完美还原 | det≠0 的方阵 |
np.linalg.det | 体检报告 | 先判断能不能还原 |
np.linalg.pinv | 尽量还原 | 还原不回去时 |
下一节预告:用逆矩阵解线性方程组(1.2.12)。
⑨ 练一练
矩阵 E =[[4,7],[2,6]]:
- 先心算 det(E),判断它能不能还原;
- 再用口诀手算它的逆矩阵;
- 最后运行下面这格核对答案。
# 练习答案(先自己想,再运行核对)E=np.array([[4,7],[2,6]])print('det(E) =',np.linalg.det(E))# 10,非零 → 能还原print('E 的逆矩阵 =\n',np.linalg.inv(E))运行结果:
det(E) = 10.000000000000002 E 的逆矩阵 = [[ 0.6 -0.7] [-0.2 0.4]]