ベクトルと行列
ベクトルは、空間内の方向や一組の量のような、数値の平らなリストです。行列は行のリストです。このページでは、内積と外積、行列の積、行列式と逆行列、複数の線形方程式を一度に解く方法、そして線形代数の数値計算ツールである固有値とQR分解、LU分解、特異値分解を説明します。
線形代数の授業、幾何学と変換、あるいは直線の方程式で結び付いた複数の未知数がある問題に役立ちます。ベクトルと行列はふつうのリストなので、インデックスなど、リストと統計の内容もすべて当てはまります。
ベクトル
dot([1, 2, 3], [4, 5, 6]) 答え 32dot([3, 4], [4, -3]) 答え 0cross([1, 0, 0], [0, 1, 0]) 答え [0, 0, 1]cross([1, 2, 3], [4, 5, 6]) 答え [-3, 6, -3]v = [3, 4]sqrt(dot(v, v)) 答え 5dot は対応する要素同士を掛けて、その結果を足します。内積がゼロなら、[3, 4] と [4, -3] のように2つのベクトルは直交しています。cross は、2つの入力の両方に直交するベクトルを返します。x方向とy方向の外積はz方向です。ベクトルの長さは、そのベクトル自身との内積の平方根です。
dot には同じ長さの2つのベクトルが、cross には要素が3つの2つのベクトルが必要です。ベクトルを足したり拡大・縮小したりするには、ふつうのリストの演算を使います。[1, 2] + [3, 4] は [4, 6]、2 * [3, 4] は [6, 8] です。
行列の書き方と積
行列は行のリストとして書き、各行をそれぞれ角かっこで囲みます。
shape([[1, 2, 3], [4, 5, 6]]) 答え [2, 3]transpose([[1, 2, 3], [4, 5, 6]]) 答え [[1, 4], [2, 5], [3, 6]]identity(3) 答え [[1, 0, 0], [0, 1, 0], [0, 0, 1]][[1, 2], [3, 4]] * [[5, 6], [7, 8]] 答え [[19, 22], [43, 50]]shape は [行数, 列数] を返します。transpose は行と列を入れ替えます。identity(n) は n 行 n 列の単位行列で、ほかの行列に掛けてもその行列を変えません。
2つの行列の間の * は行列の積です。左の行列の列数が右の行列の行数と等しい必要があります。行列にベクトルを掛けるには、ベクトルを1行に1要素の列ベクトルとして書きます。
# 点(3, 1)を反時計回りに90°回転する回転 = [[0, -1], [1, 0]]回転 * [[3], [1]] 答え [[-1], [3]]回転 * 回転 答え [[-1, 0], [0, -1]]90°の回転を2回行うと半回転になるので、回転 * 回転 では両方の符号が反転します。[3, 1] のような平らなリストは、行列の右側には置けません。名前に平らなリストが入っている場合は、transpose([v]) で列ベクトルにできます。
同じ形の行列は、ほかのリストと同じように足し算・引き算ができ、数値で掛けたり割ったりできます。
すべての行列の演算には、いくつかの規則があります。
- どの行も要素の数が同じでなければなりません。
- 要素はただの数値でなければなりません。単位やお金は使えません。
- 行列は最大32行32列です。
- セミコロンによる省略形はありません。
[1, 2; 3, 4]ではなく[[1, 2], [3, 4]]と書いてください。
行列式、逆行列、トレース、ランク
M = [[2, 1], [5, 3]]det(M) 答え 1inverse(M) 答え [[3, -1], [-5, 2]]M * inverse(M) 答え [[1, 0], [0, 1]]trace(M) 答え 5inverse([[2, 1], [1, 3]]) 答え [[0.6, -0.2], [-0.2, 0.4]]det([[1, 2], [2, 4]]) 答え 0rank([[1, 2, 3], [2, 4, 6]]) 答え 1det、inverse、trace(対角成分の和)には正方行列が必要です。行列式がゼロなら、その行列は特異です。行が独立していないため逆行列がなく、inverse([[1, 2], [2, 4]]) はエラーになります。rank は独立した行の数を数え、正方でない行列も受け付けます。最後の行では2行目が1行目の2倍なので、ランクは1です。
この4つの関数は小数ではなく厳密な分数を使って計算します。M * inverse(M) がちょうど単位行列になるのはそのためです。
連立一次方程式を解く
コーヒー3杯と紅茶2杯で¥2,110、コーヒー1杯と紅茶4杯で¥1,970だったとします。方程式ごとに1行として各飲み物の数を並べ、列の順序(コーヒー、紅茶)を固定します。合計は、それに対応するリストにします。
solve_system([[3, 2], [1, 4]], [2110, 1970]) 答え [450, 380]答えは列と同じ順序で並びます。コーヒーは¥450、紅茶は¥380です。行列の要素はただの数値でなければならないので、通貨記号は付けないでください。
solve_system(A, b) は、A * x = b を x について解きます。未知数ごとに1行の正方行列と、同じ長さの右辺の平らなリストが必要です。厳密な分数を使うので、答えに丸め誤差はありません。
方程式から答えが1つに定まらない場合は、一意の解がないというエラーになります。たとえば solve_system([[1, 2], [2, 4]], [3, 6]) は、2つ目の方程式が1つ目を2倍しただけなので失敗します。solve_system が扱えるのは直線の(線形の)方程式だけです。x^2 = 2 のような方程式には、総和、方程式の解法、微積分の solve を使ってください。
固有値
eigenvalues([[2, 1], [1, 2]]) 答え [1, 3]eigenvalues([[4, 1], [2, 3]]) 答え [5, 2]eigenvalues([[0, -1], [1, 0]]) 答え [i, -i]eigenvalues は正方行列を受け取り、平らなリストを返します。90°の回転では向きが変わらない方向がないので、固有値は複素数の組 i と -i になります。複素数を参照してください。リストの順序には依存しないでください。
行列の分解:QR、LU、SVD
どの分解も行列のリストを返します。インデックスを使って各部分を取り出します。
A = [[3, 0], [4, 5]]成分 = qr(A)q = 成分[0] 答え [[0.6, -0.8], [0.8, 0.6]]r = 成分[1] 答え [[5, 4], [0, 3]]q * r 答え [[3, 0], [4, 5]]因子を掛け合わせると再び A になるので、分解をひと目で確かめられます。2進浮動小数点で計算すると10⁻¹⁵程度のごく小さな丸め誤差が残りますが、Varligはリストや行列の中ではそれを0と表示します。保存されている値はすべての桁を保持しているので、分解の結果は == ではなく、目で見るか差を調べて比べてください。
lu([[2, 1], [4, 3]]) 答え [[[0, 1], [1, 0]], [[1, 0], [0.5, 1]], [[4, 3], [0, -0.5]]]lu は [P, L, U] を返します。P は行の入れ替えを記録したもので、P * A は L * U に等しくなります。この例では、大きいほうの数である4が先に使われるように行が入れ替えられています。
B = [[3, 1], [4, 7]]f = svd(B)u = f[0]s = f[1]vt = f[2]u * [[s[0], 0], [0, s[1]]] * vt 答え [[3, 1], [4, 7]]svd([[3, 4]]) 答え [[[1]], [5], [[0.6, 0.8]]]svd は [U, s, Vt] を返します。s は特異値を大きい順に並べた平らなリストです。教科書では B は U * diag(s) * Vt に等しいと書かれます。diag 関数はないので、[[s[0], 0], [0, s[1]]] のように対角行列を自分で組み立ててください。
| 関数 | 必要なものと返すもの |
|---|---|
identity(n) |
n 行 n 列の単位行列 |
shape(A) |
[行数, 列数] |
transpose(A) |
行と列を入れ替えた行列 |
det(A) |
正方行列の行列式 |
inverse(A) |
行列式がゼロでない正方行列の逆行列 |
trace(A) |
正方行列の対角成分の和 |
rank(A) |
独立した行の数(任意の長方形の行列) |
solve_system(A,b) |
解が1つに定まる正方行列での A * x = b の解 |
eigenvalues(A) |
正方行列の固有値(複素数の場合あり、順序は不定) |
qr(A) |
Q * R がほぼ A になる [Q, R](任意の長方形の行列) |
lu(A) |
P * A がほぼ L * U になる [P, L, U](正方行列) |
svd(A) |
[U, s, Vt]、s は平らなリスト(任意の長方形の行列) |
正方でない行列に対して、qr と svd は薄い因子を返します。3行2列の行列なら、Q は3行3列ではなく同じく3行2列になります。
アルゴリズムと制限
det、inverse、rank、solve_system は、計算機の数値の大きさの制限内で、厳密な分数を使って計算します。eigenvalues、qr、lu、svd は2進浮動小数点で計算するので、答えは精度の高い近似値です。反復は10,000ステップで打ち切られます。
[[1, 1], [1, 1.0000001]] のように行がほぼ従属している行列は、悪条件であるといいます。こうした行列で計算すると小さな丸め誤差が大きく膨らむので、分解や固有値の精度が落ちることがあり、極端な場合には数値の制限を超えてエラーになることもあります。
完全な規則は高度な数学:機能と制限にあります。
まとめて使う
市場の露店で受け取った3枚のレシートには、りんご、バナナ、みかんをそれぞれいくつ買って合計いくら払ったかは書かれていますが、個々の値段は書かれていません。
# 各行:りんご、バナナ、みかんかご = [[2, 3, 1], [1, 2, 3], [4, 1, 2]]支払額 = [520, 530, 840]det(かご) 答え 25単価 = solve_system(かご, 支払額) 答え [150, 40, 100]# どのレシートとも単価が合うか確かめるかご * transpose([単価]) 答え [[520], [530], [840]]# りんご5個、バナナ2本、みかん4個ならいくら?dot([5, 2, 4], 単価) 答え 1,230行列式がゼロでないので、レシートから値段の組が1つに定まることがわかります。りんごは¥150、バナナは¥40、みかんは¥100です。かごに列ベクトルにした単価を掛けると合計が再現され、dot で新しいかごの値段が¥1,230と求まります。