行列式の幾何的な解釈:体積拡大率

行列式の幾何的な解釈:体積拡大率#

2つのベクトルが四角形を表すとみなす。この四角形を変形させる行列Aがあったとき、変換後の四角形の面積はもとの面積のdet(A)倍である、と解釈できる

(行列式の値が正の値のとき。負の場合は軸が反転して裏返しになる。det(A)=0ならぺちゃんこに潰れている状態)

例えばIR2×2が構成する四角形にAを乗じてAI=Aとするときの拡大率ともいえるし、Aの面積とも言える

Hide code cell source
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import japanize_matplotlib

e0 = [1, 0]
e1 = [0, 1]

I = np.array([e0, e1])
O = np.array([0, 0])

fig, ax = plt.subplots(figsize=[4, 4], dpi=72)
ax.grid(True, alpha=.5)
ax.arrow(*O, *e0, width=0.01, color="black", length_includes_head=True)
ax.arrow(*O, *e1, width=0.01, color="black", length_includes_head=True)
ax.fill_between(x=[0, 1], y1=0, y2=1, alpha=.5)

ax.set(xlim=[-0.5, 1.5], ylim=[-0.5, 1.5], xticks=[0, 1], yticks=[0, 1])
fig.show()
../../../_images/0072004f3c1f9d19d4cc96524ca6d3b4517de355d3cb451d1cf70729e0e33cb3.png

適当な行列Aで変換するとこうなる

A = np.array([
    [1, 1],
    [0, 1]
])
e0_ = A @ e0
e1_ = A @ e1
y_ = e0_ + e1_
Hide code cell source
fig, ax = plt.subplots(dpi=72)
ax.grid(True, alpha=.5)
ax.arrow(0, 0, *e0_, width=0.01, color="black", length_includes_head=True)
ax.arrow(0, 0, *e1_, width=0.01, color="black", length_includes_head=True)

d = np.array([e0_, y_])
ax.plot(d[:, 0], d[:, 1], color="black", linestyle="--")
d = np.array([e1_, y_])
ax.plot(d[:, 0], d[:, 1], color="black", linestyle="--")

x = np.linspace(0, y_[0], 11)
ax.fill_between(x=x, y1=[max(e - 1, 0) for e in x], y2=[min(e, 1) for e in x], alpha=.5)
ax.set(xlim=[-0.25, 2.25], ylim=[-0.25, 1.25], xticks=[0, 1, 2], yticks=[0, 1])
fig.show()
../../../_images/5f9a3a964661ef2df4f744627f80e5b856a57ca4f366c9210acf37ad12bd6793.png

平行四辺形の面積は底辺×高さ。今回は底辺も高さも1なので、面積は変わっていない。

この行列Aの行列式は1になる

np.linalg.det(A)
1.0

参考:Chapter 6 行列式 | 線形代数のエッセンス - YouTube

(例)次のような行列の場合は…?#

A=(1213),B=(11021103)det(A)=32=1,det(B)=103102=1
A = np.array([
    [1, 2],
    [1, 3]
])
B = np.array([
    [1, 102],
    [1, 103]
])
fig, axes = plt.subplots(ncols=2, figsize=[8, 4], dpi=72)

for X, ax in zip([A, B], axes):
    x0_ = X[0]
    x1_ = X[1]
    y_ = x0_ + x1_
    ax.grid(True, alpha=.5)
    ax.arrow(0, 0, *x0_, width=0.01, color="black", length_includes_head=True)
    ax.arrow(0, 0, *x1_, width=0.01, color="black", length_includes_head=True)

    d = np.array([x0_, y_])
    ax.plot(d[:, 0], d[:, 1], color="black", linestyle="--")
    d = np.array([x1_, y_])
    ax.plot(d[:, 0], d[:, 1], color="black", linestyle="--")

    x = np.linspace(0, y_[0], 11)
    ax.set(title=f"det = {np.linalg.det(X)}")
fig.show()
../../../_images/3500d4ce8de0bad140207c915a66da3396d11dde3c225b20e9b772dc62de80fb.png

どうしてそうなるのか#

a,b を平面 R2 の 2 つのベクトルとする。 O を座標の原点とし、 A,BOA =a,OB=b であるような点とする。 OA,OB を 2 辺とする平行四辺形 OAPB を、a,bを2辺とする平行四辺形という。その面積Sを求めたい。

もしa,bが1次従属なら、OAPBは1つの線分になるのでS=0

a,bが1次独立のとき、a,bのなす角をθとすれば、 Bから辺OAに下した垂線の長さはbsinθであるから、面積は底辺×高さで

S=absinθ

両辺を2乗にする

S2=a2b2sin2θ

sin2θ=1cos2θ であり cosθ=ababなので

S2=a2b2sin2θ=a2b2(1cos2θ)=a2b2(1(ab)2a2b2)=a2b2(ab)2

a=(a1,a2)T,b=(b1,b2)Tとすれば、

S2=a2b2(ab)2=(a12+a22)(b12+b22)(a1b1+a2b2)2=(a1b2a2b1)2

ゆえに

S=|a1b2a2b1|
Hide code cell source
X = np.array([
    [2, 1],
    [0, 1]
])
a = X @ e0
b = X @ e1
P = a + b


fig, ax = plt.subplots(figsize=[4, 4], dpi=100)
ax.grid(True, alpha=.5)

ax.text(0, 0, "O", ha="right", va="top")
ax.text(*a, "A", ha="left", va="top")
ax.text(*b, "B", ha="left", va="bottom")
ax.text(*P, "P", ha="left", va="bottom")

ax.arrow(0, 0, *a, width=0.01, color="black", length_includes_head=True)
ax.arrow(0, 0, *b, width=0.01, color="black", length_includes_head=True)

d = np.array([a, P])
ax.plot(d[:, 0], d[:, 1], color="black", linestyle="--")
d = np.array([b, P])
ax.plot(d[:, 0], d[:, 1], color="black", linestyle="--")

# 垂線 y = |b| sinθ をもとめる
cos_theta = (a @ b) / (np.linalg.norm(a) * np.linalg.norm(b))
theta = np.arccos(cos_theta)
y = np.linalg.norm(b) * np.sin(theta)
ax.vlines(x=b[0], ymin=0, ymax=y, color="black", linestyle="--")
ax.text(b[0], y/2, r"$\|b\| \sin \theta$", ha="left", va="center")

# x = np.linspace(0, y_[0], 11)
# ax.fill_between(x=x, y1=[max(e - 1, 0) for e in x], y2=[min(e, 1) for e in x], alpha=.5)

ax.set(xlim=[-0.25, 3.25], ylim=[-0.25, 1.25],
       xticks=range(4), yticks=range(2))


# Drawing the angle θ near O with an arc
ax.text(0.4, 0.15, "θ", ha="center", va="center")
angle = np.linspace(0, np.arctan2(B[0], B[1]), 100)  # Calculate the angle of OB relative to the x-axis
radius = 0.3  # Radius of the arc for angle representation
x_arc = radius * np.cos(angle)
y_arc = radius * np.sin(angle)
ax.plot(x_arc, y_arc, 'k-')  

fig.show()
../../../_images/fe9a244d890155fa4baee0d8e705e92b77608c7c4bfcda3499d2e693272b22f1.png
import numpy as np

np.sin(np.pi) - np.cos(np.pi)
1.0000000000000002
- np.cos(np.pi) + np.sin(np.pi) 
1.0000000000000002