MMULT と配列数式で行列の積を求められる行列(matrix)は、数を縦横に並べた表です。
横の並びを行、縦の並びを列といいます
(Cells(行, 列) と同じ順です)。
これは素直です。大きさが同じ行列どうしでしか足せません。
規則は 1 つだけです。
答えの「i 行 j 列」は、
左の行列の i 行目(橙)と右の行列の j 列目(青)を
先頭から順に掛けて、全部足す。
上の例なら 1 行 1 列は a×e + b×g です。
掛けられる条件。 元資料は 2×2 の場合だけを扱っているので触れていませんが、 一般には左の行列の「列の数」と右の行列の「行の数」が同じでなければ掛けられません。 (m×n)×(n×p)=(m×p)── 内側の n が一致し、 答えの大きさは外側だけで決まります。 §3 の工場の問題では(1×2)×(2×2)=(1×2)になっています。
Excel には行列の積を求める MMULT 関数があります。
足し算はふつうに + で書けます。
| やること | 入れる式 |
|---|---|
| 2×2 の掛け算 | =MMULT(A1:B2, C1:D2) |
| 2×2 の足し算 | =A1:B2 + C1:D2 |
| 行列を直接書く(配列定数) | ={2,10;4,13}
── , が列の区切り、; が行の区切り |
元資料は答えを入れるとき Ctrl+Shift+Enter を押すよう指示しています。 これは複数のセルにまたがる答えを返す式(配列数式)を確定するための操作で、 当時は必須でした。 しかし Microsoft 365 以降の Excel では不要になりました (「動的配列数式」の導入により、ふつうに Enter を押すだけで 答えが必要なだけのセルに自動で広がります)。 古い Excel や互換モードでは今も必要なので、両方を知っておいてください。
この教材の処理系では MMULT は使えません。
数式を計算する仕組みを持っていないためです(第 2 章 §2)。
だから以下ではすべて VBA で計算します ──
そしてそれは遠回りではありません。
MMULT は 2 つの行列を掛けるだけですが、
VBA で書ければ何段でも、どんな大きさでも、条件をつけても計算できます。
まず、規則をそのまま書き下してみます。2×2 なら答えは 4 つなので、4 行書けば済みます。
Sub Macro1()
Dim a As Double, b As Double, c As Double, d As Double
Dim e As Double, f As Double, g As Double, h As Double
' 行列 A を A1:B2 から、行列 B を D1:E2 から読む
a = Cells(1, 1): b = Cells(1, 2)
c = Cells(2, 1): d = Cells(2, 2)
e = Cells(1, 4): f = Cells(1, 5)
g = Cells(2, 4): h = Cells(2, 5)
' 答えを G1:H2 に書く
Cells(1, 7) = a * e + b * g
Cells(1, 8) = a * f + b * h
Cells(2, 7) = c * e + d * g
Cells(2, 8) = c * f + d * h
Cells(4, 1) = "A × B を G1:H2 に入れました"
End Sub
4 つ書くのは我慢できますが、3×3 なら 9 つ、10×10 なら 100 個です。
しかも a〜h のような名前は 26 個で尽きます。
──ここが、繰り返しと配列が必要になる理由です。
同じ計算を、行と列を数え上げる形に書き直します。
まだ習っていない For が出てきますが(第 7 章)、
「中に書いたことを、i を変えながら繰り返す」と読んでください。
Sub Macro2()
Dim i As Integer, j As Integer, k As Integer
Dim n As Integer
Dim s As Double
n = 2 ' 行列の大きさ(2×2)
' i:答えの行、j:答えの列
For i = 1 To n
For j = 1 To n
s = 0
' k:左の行列の列 = 右の行列の行
For k = 1 To n
s = s + Cells(i, k).Value * Cells(k, j + 3).Value
Next k
Cells(i, j + 6) = s
Next j
Next i
Cells(4, 1) = "三重ループで A × B を求めました"
Cells(5, 1) = "掛け算した回数"
Cells(5, 2) = n * n * n
End Sub
ループが 3 つ入れ子になっています。 外の 2 つ(i, j)が「答えのどのマスを埋めるか」、 いちばん内側(k)が「掛けて足す」部分です。 だから掛け算の回数は n×n×n ── 大きさが 10 倍になると 1000 倍になります。 行列の掛け算が「重い計算」の代表とされるのはこのためで、 いまの機械学習の計算時間のほとんどは、実はこの三重ループです。
元資料 9・13 枚目の問題です。まず 1 問目。
法政工場では、車とトラックを製造しています。
車を作るためには、作業員 3 人とロボット 5 台必要です。
トラックを作るためには、作業員 4 人とロボット 8 台必要です。
車 2 台とトラック 3 台を作るために必要な作業員とロボットはいくらでしょう。
作業員 18 人、ロボット 34 台。
続いて 2 問目(元資料 14・15 枚目)。
作業員は、サンドイッチを 2 個食べ、コーヒーを 3 杯飲みます。
ロボットは、サンドイッチを 6 個、コーヒーを 10 杯燃料として必要とします。
車 2 台とトラック 3 台を作るために必要なサンドイッチとコーヒーはいくらでしょう。
サンドイッチ 240 個、コーヒー 394 杯。
ここに、行列の積のいちばん大事な意味が出ています。 元資料は 2 問を続けて計算するだけですが、この 2 問が並んでいることには理由があります。 行列は「単位の変換」で、行列の積は「変換をつなげること」なのです。
そして A×B を先に計算しておけば、 「車・トラック」から「サンドイッチ・コーヒー」へ一気に変換できます ── 途中の人数を経由しなくても同じ答えが出ます。 これが「行列の積」という演算があの形で定義されている理由です。 下で実際に確かめてください。
Sub Macro3()
Dim i As Integer, j As Integer, k As Integer
Dim s As Double
Dim v(2) As Double ' 作った台数(車, トラック)
Dim A(2, 2) As Double ' 1 台あたりの 人・ロボット
Dim B(2, 2) As Double ' 1 人・1 台あたりの サンド・コーヒー
Dim w(2) As Double ' 途中(作業員, ロボット)
Dim x(2) As Double ' 最後(サンド, コーヒー)
Dim AB(2, 2) As Double ' A と B を先に掛けたもの
' 読み込む
v(1) = Cells(2, 1): v(2) = Cells(2, 2)
For i = 1 To 2
For j = 1 To 2
A(i, j) = Cells(4 + i, j).Value
B(i, j) = Cells(4 + i, j + 3).Value
Next j
Next i
' ① 途中を通る: v × A → w、 w × B → x
For j = 1 To 2
s = 0
For k = 1 To 2
s = s + v(k) * A(k, j)
Next k
w(j) = s
Next j
For j = 1 To 2
s = 0
For k = 1 To 2
s = s + w(k) * B(k, j)
Next k
x(j) = s
Next j
Cells(8, 1) = "① 作業員": Cells(8, 2) = w(1)
Cells(9, 1) = "① ロボット": Cells(9, 2) = w(2)
Cells(8, 4) = "① サンド": Cells(8, 5) = x(1)
Cells(9, 4) = "① コーヒー": Cells(9, 5) = x(2)
' ② A×B を先に計算して、v に一気に掛ける
For i = 1 To 2
For j = 1 To 2
s = 0
For k = 1 To 2
s = s + A(i, k) * B(k, j)
Next k
AB(i, j) = s
Cells(4 + i, j + 6) = s
Next j
Next i
Cells(4, 7) = "A×B"
For j = 1 To 2
s = 0
For k = 1 To 2
s = s + v(k) * AB(k, j)
Next k
Cells(7 + j, 7) = s
Next j
Cells(7, 7) = "② 一気に"
' 一致しているか
Cells(11, 1) = "①と②は同じか"
If Cells(8, 5).Value = Cells(8, 7).Value And Cells(9, 5).Value = Cells(9, 7).Value Then
Cells(11, 2) = "同じ(結合法則)"
Cells(11, 2).Interior.Color = RGB(198, 239, 206)
Else
Cells(11, 2) = "違う"
Cells(11, 2).Interior.Color = RGB(255, 199, 206)
End If
End Sub
(v×A)×B = v×(A×B) ── これを結合法則といいます。 「途中で人数を数えてから食料を計算する」のと「変換表をあらかじめ合成しておく」のが 同じ答えになる、ということです。 コンピュータグラフィックス(第 15 章)で、拡大・回転・移動をまとめて 1 つの行列にするのも、 まったく同じ性質を使っています。
足し算や掛け算と違って、行列の積は順番を入れかえられません (AB ≠ BA)。元資料は触れていませんが、これは大事な性質です。 工場の例で考えれば当たり前で ── 「車 → 人 → 食料」の順に変換するのが正しいのに、 「車 → 食料 → 人」と掛けたら意味をなしません。 行列の積は「変換の順序」を表しているので、順番に意味があるのです。
Sub Macro4()
Dim i As Integer, j As Integer, k As Integer
Dim s As Double
Dim onaji As Boolean
' A×B を G1:H2 に
For i = 1 To 2
For j = 1 To 2
s = 0
For k = 1 To 2
s = s + Cells(i, k).Value * Cells(k, j + 3).Value
Next k
Cells(i, j + 6) = s
Next j
Next i
Cells(4, 7) = "↑ A×B"
' B×A を G5:H6 に(左右を入れかえるだけ)
For i = 1 To 2
For j = 1 To 2
s = 0
For k = 1 To 2
s = s + Cells(i, k + 3).Value * Cells(k, j).Value
Next k
Cells(i + 5, j + 6) = s
Next j
Next i
Cells(8, 7) = "↑ B×A"
onaji = True
For i = 1 To 2
For j = 1 To 2
If Cells(i, j + 6).Value <> Cells(i + 5, j + 6).Value Then onaji = False
Next j
Next i
Cells(4, 1) = "A×B と B×A は同じか"
If onaji Then
Cells(5, 1) = "同じだった(たまたま)"
Else
Cells(5, 1) = "違う ── 順番を変えられない"
Cells(5, 1).Interior.Color = RGB(255, 235, 156)
End If
End Sub
MMULT。Microsoft 365 では Ctrl+Shift+Enter は不要になったもとにしたのは「情報学入門(VBA)120-2019」全 15 枚です。
| 元資料 | この教材 | 種別 |
|---|---|---|
| 配列数式の確定に Ctrl+Shift+Enter を要求している | Microsoft 365 以降では不要(動的配列数式)。古い Excel では今も必要なので両方を記述 | 古い |
| 2×2 の場合だけを扱い、掛けられる条件(左の列数=右の行数)に触れていない | (m×n)×(n×p)=(m×p)として明示。工場の問題が(1×2)×(2×2)であることも示した | 不足 |
| 工場の問題を 2 問続けて計算するが、2 問が並んでいる意味を説明していない | 行列は単位の変換、積は変換の合成。結合法則を実際に確かめるモデルを追加 | 不足 |
| AB ≠ BA(交換法則が成り立たない)に触れていない | §4.1 として追加。工場の例で「順序に意味がある」ことと結びつけた | 不足 |
| VBA での書き方が画面の画像で示されるだけで、一般化(二重・三重ループ)への道筋がない | 規則そのままの 4 行版 → 三重ループ版と並べ、なぜループが必要かを示した。計算量 n3 にも言及 | 不足 |
| 「型宣言は、省略すると Variant 型となる」(第 3 章と重複) | 第 3 章に集約。この章では配列(Dim A(2, 2) As Double)の形で使った | 重複 |
元資料の計算はすべて正しいことを検算しました。
2×2 の積の展開(ae+bg など)、配列定数の書き方(, が列・; が行)、
=MMULT(A1:B2,C1:D2)、=A1:B2+C1:D2、
工場の問題の答え(2 3)×(3 5 / 4 8)=(18 34)と(18 34)×(2 3 / 6 10)=(240 394)──
いずれも正確です。