内積は計算方法としてはそれほど難しいものではありませんが、色々な応用があるためイメージしにくい計算です。幾何学的な意味付けがありますが、そのように解釈しにくい計算にも応用されます。例として、日常的にも触れる機会の多い計算書(見積りなど)を取り上げます。NumPyによる計算を添えます。
見積り
文房具を調達するケースを考えます。
※ 消費税は無視します。
品名 | 単価 | 個数 | 小計 |
---|---|---|---|
鉛筆 | 30 | 12 | 360 |
消しゴム | 50 | 10 | 500 |
総計 | 860 |
このような計算書は日常的によく見掛けるものです。計算を式で書き直してみます。
30×12+50×10=360+500=860
単価と数量をベクトルで書き直せば、これは内積の計算です。
\left(\begin{matrix}30 \\ 50\end{matrix}\right)\cdot
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
=30×12+50×10=860
このように表計算をベクトルで書き直したと捉えることが可能です。このような用途で使われるベクトルや内積を幾何学的に解釈することにはあまり意味がありません。
NumPy
NumPyでの計算を示します。2種類の書き方ができます。
>>> from numpy import *
>>> dot(array([30, 50]), array([12, 10]))
860
>>> array([30, 50]).dot(array([12, 10]))
860
※ 以後import
は省略します。
NumPyでは演算子*
は小計に相当します。
>>> array([30, 50]) * array([12, 10])
array([360, 500])
※ 数学でこのような形の積はあまり見掛けませんが、アダマール積という名前が付いています。
小計を足せば総計が得られます。これは内積と同じ計算です。
>>> sum(array([30, 50]) * array([12, 10]))
860
>>> dot(array([30, 50]), array([12, 10]))
860
※ Excelでこのような表計算をした経験があれば、SUM
は見慣れているでしょう。
横ベクトル
$\vec{a}\cdot\vec{b}$ という内積を、$\vec{b}$ に対して左から $\vec{a} \cdot$ が作用するという風に捉えます。$\vec{a}$ を転置すれば積として表現できます。
\vec{a}\cdot\vec{b}
=\left(\begin{matrix}a_1 \\ a_2\end{matrix}\right)\cdot
\left(\begin{matrix}b_1 \\ b_2\end{matrix}\right)
=\left(\begin{matrix}a_1 \\ a_2\end{matrix}\right)^{\top}
\left(\begin{matrix}b_1 \\ b_2\end{matrix}\right)
=\left(\begin{matrix}a_1 & a_2\end{matrix}\right)
\left(\begin{matrix}b_1 \\ b_2\end{matrix}\right)
「横ベクトル掛ける縦ベクトル」はベクトルの内積の別表現だと意識しておくと良いです。
※ 縦ベクトルに左から掛かる横ベクトルはコベクトルや1-形式と呼んで特別な意味付けがなされます。
相見積り
複数の仕入れ先(A社・B社)から相見積りを取るケースを考えます。
品名 | 単価(A社) | 単価(B社) | 個数 | 小計(A社) | 小計(B社) |
---|---|---|---|---|---|
鉛筆 | 30 | 25 | 12 | 360 | 300 |
消しゴム | 50 | 60 | 10 | 500 | 600 |
総計 | 860 | 900 |
これは2つの内積計算で表せます。
\begin{align}
\left(\begin{matrix}30 \\ 50\end{matrix}\right)\cdot
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
&=30×12+50×10=860 \\
\left(\begin{matrix}25 \\ 60\end{matrix}\right)\cdot
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
&=25×12+60×10=900
\end{align}
個数のベクトルが共通していることに注目して、これらを1つの式にくっ付けることを考えます。単価のベクトルを束ねて行列にしますが、行列とベクトルとの内積はないため、横ベクトルで考えたのと同じ要領で転置します。
\begin{align}
\left(\begin{array}{c|c}30 & 25 \\ 50 & 60\end{array}\right)^{\top}
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
&=\left(\begin{matrix}30 & 50 \\ \hline 25 & 60\end{matrix}\right)
\left(\begin{matrix}12 \\ 10\end{matrix}\right) \\
&=\left(\begin{array}{c|c}30×12+50×10 \\ 25×12+60×10\end{array}\right) \\
&=\left(\begin{matrix}860 \\ 900\end{matrix}\right)
\end{align}
※ 区切り線は補助です。行列は基本的に縦に区切られて、転置したときには区切りも横になることを表します。
左辺と表計算を見比べれば、同じものを表現していることが分かるでしょうか。このように行列とベクトルの積によって複数の内積を計算していると解釈できます。
※ 縦ベクトルに左から掛かる転置した行列は2-形式と呼ばれることがあります(1-形式との対比)。
NumPy
行列は配列の配列(2次元配列)として記述します。
>>> array([[30, 25], [50, 60]])
array([[30, 25],
[50, 60]])
\left(\begin{matrix}30 & 25 \\ 50 & 60\end{matrix}\right)
行列の積もdot
で計算します。転置は.T
です。
>>> dot(array([[30, 25], [50, 60]]).T, array([12, 10]))
array([860, 900])
\left(\begin{matrix}30 & 25 \\ 50 & 60\end{matrix}\right)^{\top}
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
=\left(\begin{matrix}860 \\ 900\end{matrix}\right)
業者ごとに単価を記述すると考えれば、転置なしでダイレクトに記述できます。
>>> dot(array([[30, 50], [25, 60]]), array([12, 10]))
array([860, 900])
\left(\begin{matrix}30 & 50 \\ 25 & 60\end{matrix}\right)
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
=\left(\begin{matrix}860 \\ 900\end{matrix}\right)
どちらが良いかはケースバイケースですが、表計算ベースで考えるなら転置を使った方が分かりやすいでしょう。
個数の比較
個数を変えて比較するケースを考えます。
品名 | 単価 | 個数1 | 小計1 | 個数2 | 小計2 |
---|---|---|---|---|---|
鉛筆 | 30 | 12 | 360 | 9 | 270 |
消しゴム | 50 | 10 | 500 | 13 | 650 |
総計 | 860 | 920 |
これは2つの内積計算で表せます。
\begin{align}
\left(\begin{matrix}30 \\ 50\end{matrix}\right)\cdot
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
&=30×12+50×10=860 \\
\left(\begin{matrix}30 \\ 50\end{matrix}\right)\cdot
\left(\begin{matrix} 9 \\ 13\end{matrix}\right)
&=30×9+50×13=920
\end{align}
単価のベクトルが共通していることに注目して、これらを1つの式にくっ付けることを考えます。個数のベクトルを束ねて行列にします。ベクトルと行列との内積はありませんが、左のベクトルを転置すれば自然と計算できます。
\begin{align}
\left(\begin{matrix}30 \\ 50\end{matrix}\right)^{\top}
\left(\begin{array}{c|c}12 & 9 \\ 10 & 13\end{array}\right)
&=\left(\begin{matrix}30 & 50\end{matrix}\right)
\left(\begin{array}{c|c}12 & 9 \\ 10 & 13\end{array}\right) \\
&=\left(\begin{array}{c|c}30×12+50×10 & 30×9+50×13\end{array}\right) \\
&=\left(\begin{array}{c|c}860 & 920\end{array}\right)
\end{align}
NumPy
NumPyのベクトルには縦横の区別がなく、ケースバイケースで適切に処理されます。dot
の第1引数にベクトルを与えれば、横ベクトルと見なして積が計算されます。
※ 内積と積とを区別せずにdot
に統合されているのは、自動で区別すれば楽だという意図だと思われます。内積を表すinner
という関数もありますが、挙動はdot
と同じです。
>>> dot(array([30, 50]), array([[12, 9], [10, 13]]))
array([860, 920])
\left(\begin{matrix}30 & 50\end{matrix}\right)
\left(\begin{matrix}12 & 9 \\ 10 & 13\end{matrix}\right)
=\left(\begin{matrix}860 & 920\end{matrix}\right)
NumPyの計算結果が縦横不明な単なるベクトルなのに注意が必要です。明示的に横ベクトル(1行2列の行列)を与えれば計算結果も横ベクトルとなります。
>>> dot(array([[30, 50]]), array([[12, 9], [10, 13]]))
array([[860, 920]])
相見積りで個数の比較
相見積りで個数も比較してみましょう。
品名 | 単価 (A社) |
単価 (B社) |
個数1 | 小計1 (A社) |
小計1 (B社) |
個数2 | 小計2 (A社) |
小計2 (B社) |
---|---|---|---|---|---|---|---|---|
鉛筆 | 30 | 25 | 12 | 360 | 300 | 9 | 270 | 225 |
消しゴム | 50 | 60 | 10 | 500 | 600 | 13 | 650 | 780 |
総計 | 860 | 900 | 920 | 1,005 |
これは4つの内積計算で表せます。
\begin{align}
\left(\begin{matrix}30 \\ 50\end{matrix}\right)\cdot
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
&=30×12+50×10=860 \\
\left(\begin{matrix}25 \\ 60\end{matrix}\right)\cdot
\left(\begin{matrix}12 \\ 10\end{matrix}\right)
&=25×12+60×10=900 \\
\left(\begin{matrix}30 \\ 50\end{matrix}\right)\cdot
\left(\begin{matrix} 9 \\ 13\end{matrix}\right)
&=30×9+50×13=920 \\
\left(\begin{matrix}25 \\ 60\end{matrix}\right)\cdot
\left(\begin{matrix} 9 \\ 13\end{matrix}\right)
&=25×9+60×13=1005
\end{align}
単価や個数のベクトルが共通していることに注目して、これらを1つの式にくっ付けることを考えます。ベクトルを束ねて行列にします。行列と行列との内積はありませんが、左の行列を転置すれば自然と計算できます。
\begin{align}
\left(\begin{array}{c|c}30 & 25 \\ 50 & 60\end{array}\right)^{\top}
\left(\begin{array}{c|c}12 & 9 \\ 10 & 13\end{array}\right)
&=\left(\begin{matrix}30 & 50 \\ \hline 25 & 60\end{matrix}\right)
\left(\begin{array}{c|c}12 & 9 \\ 10 & 13\end{array}\right) \\
&=\left(\begin{array}{c|c}30×12+50×10 & 30×9+50×13\\
25×12+60×10 & 25×9+60×13\end{array}\right) \\
&=\left(\begin{array}{c|c}860 & 920 \\ 900 & 1005\end{array}\right)
\end{align}
NumPy
特に新出事項はありません。
>>> dot(array([[30, 25], [50, 60]]).T, array([[12, 9], [10, 13]]))
array([[ 860, 920],
[ 900, 1005]])
\left(\begin{matrix}30 & 25 \\ 50 & 60\end{matrix}\right)^{\top}
\left(\begin{matrix}12 & 9 \\ 10 & 13\end{matrix}\right)
=\left(\begin{matrix}860 & 920 \\ 900 & 1005\end{matrix}\right)
計算書を行列計算に見立てる説明は以上です。
関連記事
コベクトルの考え方は双対ベクトル空間に由来します。
歴史的には内積や外積は四元数に由来します。四元数を掛け算すれば、内積と外積が同時に得られます。
- 四元数と行列で見る内積と外積の「内」と「外」 2016.11.02
四元数の積を扱いやすい形で一般化したのがクリフォード代数の幾何学積です。外積を中心に追う記事です。
- 外積と愉快な仲間たち 2016.10.26
参考
NumPyについて参考にさせていただきました。
- @keisuke-nakata: numpyの1d-arrayを2d-arrayに変換 - keisukeのブログ 2014.09.20
- @Yunosuke21: numpyで行列の転置と、行列同士の掛け算をしよう。 2016.09.20
MathJaxについて参考にさせていただきました。