結論

内積は2つのベクトルから「数」を、外積は両方に垂直な「ベクトル」を作る計算です。内積は cos、外積は sin と結びつきます。

この記事でわかること

  • 公式4つの手計算とNumPyでの一致確認
  • 角度・直交・射影・面積・左右判定の5つの使い方
  • ゲーム・法線・類似度のコードと間違い8種

2026年10月22日更新|Python 3.11・NumPy 2.4.6で実行確認

同じ2つのベクトル a と b から、内積は「どれくらい同じ向きか」を表す数を、外積は「2つに垂直な向き」と「平行四辺形の面積」を持つベクトルを作ります。

flowchart TD
    A[2つのベクトル a と b] --> B[内積 a・b<br>答えは数 スカラー]
    A --> C[外積 a×b<br>答えはベクトル]
    B --> D[角度・直交・射影<br>cos を使う]
    C --> E[垂直な向き・面積<br>sin を使う]

急いでいる人向けに、よく調べられることの答えを先にまとめます(例は a = (1, 2, 3)、b = (4, 5, 6))。

知りたいこと 答え 例の値
内積の公式(成分) a1b1 + a2b2 + a3b3 4 + 10 + 18 = 32
内積の公式(角度) a の大きさ × b の大きさ × cosθ cosθ = 0.9746、θ ≒ 12.93度
外積の公式(成分) (a2b3 − a3b2, a3b1 − a1b3, a1b2 − a2b1) (−3, 6, −3)
外積の大きさ 平行四辺形の面積(大きさの積 × sinθ) √54 ≒ 7.348
内積が0 2つのベクトルは垂直(直交) (2, 1)・(−1, 2) = 0
2次元の外積 a1b2 − a2b1(数)。正なら b は a の左 (3, 1)×(1, 2) = 5
NumPy で a @ b(内積)、np.cross(a, b)(外積) 32、[−3 6 −3]

確認環境:Ubuntu 24.04・Python 3.11.15・NumPy 2.4.6(math.sumprod の比較のみ Python 3.12.3)・Node.js 22.22.2。記事の計算例はすべて手で計算したうえで NumPy で実行し、値が一致することを確かめました。NumPy のエラー文・警告文は実際に出た原文です。関数の仕様は NumPy 公式ドキュメント(numpy.dot・numpy.cross・numpy.linalg.cross)、ゲームエンジンの座標系は Unity 6.6 の Scripting API(Vector3.Cross・Vector3.Dot)、Unreal Engine 5.8 のドキュメント、Godot の GitHub にあるクラスリファレンス(Vector2・Vector3)で、定義と性質は Wikipedia「ドット積」「クロス積」で確かめました(確認日 2026年10月22日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「内積・外積とは|公式と計算例・使い道をNumPyで実演」

内積と外積とは(違いを1枚の表で)

この節では、内積と外積がそれぞれ何を計算するもので、どこが違うのかが分かります。

内積(ないせき)は、2つのベクトルから1つの数(スカラー)を作る計算です。外積(がいせき)は、2つの3次元ベクトルから、両方に垂直な新しいベクトルを作る計算です。 ベクトルは「向きと大きさを持つ量」で、(1, 2, 3) のように数の組で表します。

同じ a = (1, 2, 3)、b = (4, 5, 6) で計算すると

内積 a・b答えは 32 という1つの数。a と b がどれくらい同じ向きかを表す。順番を入れかえても同じ
→
外積 a×b答えは (−3, 6, −3) というベクトル。a にも b にも垂直で、長さは平行四辺形の面積。順番を入れかえると向きが逆

内積は「数」、外積は「ベクトル」。ここがいちばん大きな違い

項目 内積 外積
英語 dot product・inner product cross product・vector product
ほかの呼び名 ドット積・スカラー積・点乗積 クロス積・ベクトル積
記号 a・b(点) a×b(バツ)
答えの種類 数(スカラー) ベクトル
使える次元 何次元でも 3次元(2次元は数を返す変わり形)
角度との関係 大きさの積 × cosθ 大きさの積 × sinθ(長さ)
順番を入れかえると 同じ(a・b = b・a) 向きが逆(b×a = −a×b)
0 になるとき 垂直なとき 平行なとき
わかること 角度・直交・射影・類似度 垂直な向き(法線)・面積・左右

外積はアメリカの物理学者ギブズとイギリスのヘヴィサイドが、それぞれ独立に考えたと Wikipedia「クロス積」は説明しています。

「外積」と outer product(直積)は別もの

日本語の「外積」は、ふつうこの記事のクロス積(cross product)を指します。一方、英語の outer product(NumPy の np.outer)も日本語で「外積」と訳されることがあり、こちらは全成分の組み合わせを掛けた行列を作る別の計算です。直積・テンソル積とも呼ばれます。

import numpy as np
a = np.array([1, 2, 3]); b = np.array([4, 5, 6])
print(np.cross(a, b))   # クロス積(この記事の外積)
print(np.outer(a, b))   # outer product(直積)
[-3  6 -3]
[[ 4  5  6]
 [ 8 10 12]
 [12 15 18]]

np.outer は m 次元と n 次元のベクトルから m 行 n 列の行列を作ります。数学の「外積代数(楔積、ウェッジ積)」もまた別の考え方で、3次元のときだけクロス積と対応します。検索や本で「外積」を見たら、答えがベクトル(cross)か行列(outer)かで見分けます。

内積の公式(成分の公式と、大きさ・角度の公式)

この節では、内積の2つの公式と、2つが同じ値になる理由が分かります。

内積には「成分で計算する公式」と「大きさと角度で計算する公式」の2つがあり、どちらで計算しても同じ値になります。

成分の公式(2次元)  a・b = a1×b1 + a2×b2
成分の公式(3次元)  a・b = a1×b1 + a2×b2 + a3×b3
成分の公式(n次元)  a・b = a1×b1 + a2×b2 + … + an×bn

大きさと角度の公式    a・b = |a| |b| cosθ
                     θ は a と b のなす角(0度 ≦ θ ≦ 180度)
                     |a| は a の大きさ(長さ) = √(a1² + a2² + a3²)
なす角を求める式      cosθ = a・b ÷ ( |a| |b| )

成分の公式は「同じ位置の成分どうしを掛けて、全部足す」だけです。a か b が零ベクトル(長さ0のベクトル)のときは、角度が決まらないので、内積は 0 と決めます。

2つの公式が同じになる理由(余弦定理)

三角形の余弦定理を使うと、角度の公式から成分の公式が出てきます。a と b を2辺とする三角形の、残りの辺は b − a です。

余弦定理         |b − a|² = |a|² + |b|² − 2 |a| |b| cosθ
左辺を成分で書く  (b1 − a1)² + (b2 − a2)² = (a1² + a2²) + (b1² + b2²) − 2(a1b1 + a2b2)
2つを比べる       |a| |b| cosθ = a1b1 + a2b2

3次元でも同じ計算で a1b1 + a2b2 + a3b3 になります。成分の公式は計算に、角度の公式は意味を考えるのに使います。

内積の性質

性質 式 NumPy で確かめた値(a=(1,2,3)・b=(4,5,6))
交換法則 a・b = b・a どちらも 32
自分自身との内積 a・a = 大きさの2乗 14(= √14 の2乗)
分配法則 a・(b + c) = a・b + a・c 成り立つ
数倍 (ka)・b = k(a・b) 成り立つ
和の大きさの2乗 大きさ(a+b)² = 大きさa² + 2a・b + 大きさb² 155 = 14 + 64 + 77
コーシー・シュワルツの不等式 内積の絶対値 ≦ 大きさの積 乱数10万組で全部成り立った

最後の不等式があるので、a・b ÷ (|a| |b|) は必ず −1〜1 に入り、cosθ として読めます(ただし計算機では少しはみ出すことがあります。後の「よくある間違い」で実測します)。

内積の計算例(手計算→NumPyで一致を確かめる)

この節では、2次元と3次元の内積を手で計算し、なす角まで出す流れと、NumPy で同じ値になることが分かります。

2次元の例:a = (3, 1)、b = (1, 2)

1

成分どうしを掛けて足す

a・b = 3 × 1 + 1 × 2 = 3 + 2 = 5。

a・b = 5

2

大きさを出す

|a| = √(9 + 1) = √10、|b| = √(1 + 4) = √5。

√10 と √5

3

cosθ を出す

cosθ = 5 ÷ (√10 × √5) = 5 ÷ √50 = 5 ÷ (5√2) = 1 ÷ √2。

cosθ = 1/√2

4

角度にする

cosθ = 1/√2 になる角度は 45度。a と b のなす角は 45度です。

θ = 45度

NumPy で同じ計算をしました。

import numpy as np

a = np.array([3, 1]); b = np.array([1, 2])
print(np.dot(a, b), a @ b, np.sum(a * b))          # 内積を3通りで
na, nb = np.linalg.norm(a), np.linalg.norm(b)        # 大きさ
cos = a @ b / (na * nb)
print(na, nb, cos, np.degrees(np.arccos(cos)))
5 5 5
3.1622776601683795 2.23606797749979 0.7071067811865475 45.00000000000001

内積 5、cosθ = 0.70710678…(1/√2)、θ = 45度で、手計算と一致しました。 角度が 45.00000000000001 になったのは、浮動小数点数の丸め誤差です。

3次元の例:a = (1, 2, 3)、b = (4, 5, 6)

a・b = 1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32
|a| = √(1 + 4 + 9) = √14 ≒ 3.742
|b| = √(16 + 25 + 36) = √77 ≒ 8.775
cosθ = 32 ÷ √(14 × 77) = 32 ÷ √1078 ≒ 0.9746
θ ≒ 12.93度
[3D] a·b = 32
cosθ = 0.9746318461970762  θ(度) = 12.933154491899135

NumPy の出力も 32・0.9746・12.93度で、手計算と同じでした。a と b はかなり近い向き(約13度の差)だと分かります。

内積でわかること(角度・直交・射影)

この節では、内積の値から読み取れる3つのこと(向きの近さ、垂直かどうか、影の長さ)が分かります。

内積の符号で、なす角が鋭角か鈍角かが分かる

大きさは必ず0以上なので、内積の符号(プラス・マイナス)は cosθ の符号と同じです。

内積の値 なす角 θ 意味
プラス 0度以上 90度未満(鋭角) だいたい同じ向き
0 90度 垂直(直交)
マイナス 90度より大きく 180度以下(鈍角) だいたい反対向き
大きさの積と同じ 0度 まったく同じ向き
大きさの積のマイナス 180度 まったく反対向き

角度と cosθ(内積 ÷ 大きさの積)、sinθ(外積の長さ ÷ 大きさの積)の値をグラフにしました。右へ下がっていく線が cosθ、山の形の線が sinθ です。

---
config:
  xyChart:
    width: 480
    height: 300
---
xychart-beta
    title "なす角と cosθ(内積)・sinθ(外積の長さ)"
    x-axis "なす角(度)" [0, 30, 60, 90, 120, 150, 180]
    y-axis "値" -1 --> 1
    line [1, 0.866, 0.5, 0, -0.5, -0.866, -1]
    line [0, 0.5, 0.866, 1, 0.866, 0.5, 0]

cosθ は 90度で 0 になり、それより大きい角度でマイナスになります。sinθ は 0度と 180度(平行)で 0、90度で最大です。 内積は「向きが近いほど大きい」、外積の長さは「垂直に近いほど大きい」と覚えられます。

内積が0なら垂直(直交の判定)

零ベクトルでない2つのベクトルは、内積が0のとき、またそのときに限り垂直です。(2, 1) と (−1, 2) は 2 × (−1) + 1 × 2 = 0 なので垂直です。NumPy の出力も (2,1)·(-1,2) = 0 でした。

角度を計算しなくても、掛け算と足し算だけで垂直かどうかが分かるので、プログラムでよく使います。ただし小数のベクトルでは 0 ぴったりにならないことがあります(後の節で実測します)。

射影(影の長さ)

b を a の向きに投影した影の長さ(正射影の長さ)は、a・b ÷ |a| です。 影そのもののベクトル(射影ベクトル)は、(a・b ÷ a・a) × a です。

a = np.array([3, 1]); b = np.array([1, 2])
print(a @ b / np.linalg.norm(a))     # b を a へ投影した長さ
print((a @ b) / (a @ a) * a)         # 射影ベクトル
1.5811388300841895
[1.5 0.5]

影の長さは 5 ÷ √10 ≒ 1.581、射影ベクトルは (5 ÷ 10) × (3, 1) = (1.5, 0.5) です。b から射影ベクトルを引いた (−0.5, 1.5) は a と垂直になります(3 × (−0.5) + 1 × 1.5 = 0)。ベクトルを「a の向きの成分」と「a に垂直な成分」に分けるこの計算は、坂道に沿った力の分解や、ゲームで壁に沿って滑らせる動きに使われます。データ分析の主成分分析も、分散が最大になる向きへデータを射影する方法です。その手順は、関連記事「主成分分析(PCA)とは|手順・寄与率・負荷量の読み方をPythonで実演」で扱っています。

物理の仕事 W も内積で、力 F = (10, 0, 0) N で (3, 4, 0) m 動いたときの仕事は F・d = 30 J になりました(力の向きの成分 3 m だけが仕事になる)。

外積の公式(成分・大きさ・向き)

この節では、外積の成分の公式と、その結果のベクトルの大きさ・向きが分かります。

外積 a×b の成分は (a2b3 − a3b2, a3b1 − a1b3, a1b2 − a2b1) です。 結果のベクトルは a と b の両方に垂直で、長さは a と b が作る平行四辺形の面積と同じです。

成分の公式    a×b = ( a2×b3 − a3×b2 ,  a3×b1 − a1×b3 ,  a1×b2 − a2×b1 )

行列式の形    a×b = | e1  e2  e3 |
                    | a1  a2  a3 |
                    | b1  b2  b3 |   (e1, e2, e3 は x・y・z 方向の基本ベクトル)

大きさ        |a×b| = |a| |b| sinθ   … a と b が作る平行四辺形の面積
向き          a にも b にも垂直。a → b の向きに右ねじを回したとき、ねじが進む向き(右手系)

外積の手計算(たすき掛けの手順)

a = (1, 2, 3)、b = (4, 5, 6) で計算します。成分を「y と z」「z と x」「x と y」の順に、たすき掛けして引きます。

1

2行に並べる

上の行に a の成分 1・2・3、下の行に b の成分 4・5・6 を書きます。

a の行と b の行

2

x 成分:x の列を隠す

残った y・z の列で、左上×右下 − 右上×左下。2 × 6 − 3 × 5 = 12 − 15 = −3。

x = −3

3

y 成分:z と x の順で

z・x の順に並べて同じように。3 × 4 − 1 × 6 = 12 − 6 = 6。x・z の順で計算するときは符号を逆にします。

y = 6

4

z 成分:z の列を隠す

残った x・y の列で、1 × 5 − 2 × 4 = 5 − 8 = −3。

z = −3

5

内積で検算する

(−3, 6, −3)・a = −3 + 12 − 9 = 0、(−3, 6, −3)・b = −12 + 30 − 18 = 0。両方0なら垂直で、計算は合っています。

a×b = (−3, 6, −3)

覚え方のこつは、x の成分は「y と z」、y の成分は「z と x」、z の成分は「x と y」と、x → y → z → x の輪の順に2つずつ使うことです。 最後の内積の検算を習慣にすると、符号の間違いをその場で見つけられます。

向きは右手(右ねじ)で決まる

右手の親指を a、人差し指を b に向けると、中指の向きが a×b です(右手系)。右ねじを a から b の向きに回したとき、ねじが進む向きとも言えます。基本ベクトルで確かめると、次のようになりました。

ex×ey = [0 0 1]  ey×ez = [1 0 0]  ez×ex = [0 1 0]  ey×ex = [ 0  0 -1]

x × y = z、y × z = x、z × x = y で、順番を逆にした y × x は −z です。

外積の計算例(手計算→NumPyで一致を確かめる)

この節では、前の節の手計算を NumPy で実行し、大きさ(面積)と角度の関係まで数値で確かめた結果が分かります。

import numpy as np

a = np.array([1, 2, 3]); b = np.array([4, 5, 6])
c = np.cross(a, b)
print(c, np.cross(b, a))                       # a×b と b×a
print(c @ a, c @ b)                            # a にも b にも垂直か
print(np.linalg.norm(c))                       # 大きさ(面積)
th = np.arccos(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))
print(np.linalg.norm(a) * np.linalg.norm(b) * np.sin(th))   # |a||b|sinθ
print(c @ c + (a @ b) ** 2, (a @ a) * (b @ b))              # ラグランジュの恒等式
[-3  6 -3] [ 3 -6  3]
0 0
7.3484692283495345
7.3484692283495425
1078 1078
(−3, 6, −3)a×b(手計算と同じ)b×a は (3, −6, 3)
0と0a・(a×b) と b・(a×b)両方に垂直
7.348外積の長さ = √54大きさの積 × sinθ と同じ

外積は手計算の (−3, 6, −3) と一致し、a・b のどちらとの内積も 0、長さは √54 ≒ 7.348 で |a| |b| sinθ と小数点以下13桁まで同じでした。 最後の行は「外積の長さの2乗 + 内積の2乗 = 大きさの積の2乗」(ラグランジュの恒等式)で、54 + 1024 = 1078 = 14 × 77 です。sin² + cos² = 1 を言いかえた式で、内積と外積が1組で「角度の情報」を全部持っていることを表しています。

外積でわかること(垂直なベクトル・平行四辺形の面積・右手系)

この節では、外積の結果から、平面の法線・面積・平行かどうか・体積が分かる理由と計算例が分かります。

12つに垂直なベクトル

a と b を含む平面に垂直な向き(法線)がそのまま得られる。長さで割ると単位法線ベクトル。3Dの面の向き・光の計算に使う。

2平行四辺形・三角形の面積

外積の長さが平行四辺形の面積。三角形はその半分。3つの頂点の座標だけで面積が出る。

3平行かどうか

平行(同じ向き・逆向き)なら外積は零ベクトル。sinθ = 0 だから。点が直線上にあるかの判定にも使う。

4回る向き(右手系)

a から b へ左回りか右回りかが、外積の向き(2次元なら符号)で分かる。力のモーメント(トルク)もこの向き。

三角形の面積と平面の式

3点 (1, 0, 0)・(0, 2, 0)・(0, 0, 3) を通る三角形の、法線と面積を求めます。2辺のベクトルは (−1, 2, 0) と (−1, 0, 3) です。

法線 = (−1, 2, 0) × (−1, 0, 3)
     = ( 2×3 − 0×0 , 0×(−1) − (−1)×3 , (−1)×0 − 2×(−1) ) = (6, 3, 2)
面積 = |(6, 3, 2)| ÷ 2 = √(36 + 9 + 4) ÷ 2 = 7 ÷ 2 = 3.5
平面の式 = 6x + 3y + 2z = 6     (法線・点 (1,0,0) = 6)
三角形 (1,0,0)(0,2,0)(0,0,3) の法線 = [6. 3. 2.]  面積 = 3.5
平面の式:  [6. 3. 2.] · (x,y,z) = 6.0

NumPy でも法線 (6, 3, 2)・面積 3.5 になりました。法線と1点が分かれば、平面の式「法線・(x, y, z) = 一定」がすぐに作れます。

平行なら零ベクトル

a×a = [0 0 0]  平行 a×(2a) = [0 0 0]

自分自身や、数倍したベクトルとの外積は (0, 0, 0) です。ただし小数の計算では、ほぼ平行でも 0 ぴったりにならないので、長さが小さい値以下かで判定します。

3つのベクトル:スカラー三重積は平行六面体の体積

a・(b×c) をスカラー三重積と呼び、その絶対値は a・b・c が作る平行六面体(傾いた箱)の体積です。 3行3列の行列式と同じ値になります。c = (0, 1, 1) を足して計算しました。

スカラー三重積 a·(b×c) = 3  det = 3.0
ベクトル三重積 a×(b×c) = [20 -7 -2]  b(a·c)−c(a·b) = [20 -7 -2]
結合法則 (a×b)×c = [ 9  3 -3]
ヤコビ恒等式 = [0 0 0]
分配 a×(b+c) = [-4  5 -2]  a×b+a×c = [-4  5 -2]
性質 式 実行結果
反交換法則 b×a = −(a×b) (3, −6, 3) と (−3, 6, −3)
分配法則 a×(b + c) = a×b + a×c どちらも (−4, 5, −2)
結合法則は成り立たない a×(b×c) と (a×b)×c は別 (20, −7, −2) と (9, 3, −3)
ベクトル三重積 a×(b×c) = b(a・c) − c(a・b) どちらも (20, −7, −2)
ヤコビ恒等式 3つを順に回した三重積の和 = 0 (0, 0, 0)
スカラー三重積 a・(b×c) = 行列式 どちらも 3

外積は3次元(と7次元)でしか、この形のベクトルとして定義できない、と Wikipedia「クロス積」は説明しています。4次元以上で「垂直なベクトル」が欲しいときは、行列式や外積代数など別の道具を使います。

2次元の外積(符号で左右・時計回りを判定する)

この節では、2次元のベクトルで使う「外積」の正体と、符号で左右・回る向きを判定する方法、画面座標で符号の意味が逆に見える理由が分かります。

2次元のベクトル a = (a1, a2)、b = (b1, b2) の外積は a1b2 − a2b1 という1つの数として使います。 z 成分を 0 にして3次元の外積を計算したときの z 成分と同じです。

2次元の外積   a×b = a1×b2 − a2×b1
             = |a| |b| sinθ   (θ は a から b へ左回りに測った角。符号つき)
符号          プラス → b は a の左側(a から b へ左回り=反時計回り)
             マイナス → b は a の右側(右回り=時計回り)
             0 → 平行(同じ直線上)
絶対値        a と b が作る平行四辺形の面積

a = (3, 1)、b = (1, 2) なら 3 × 2 − 1 × 1 = 5 で、プラスなので b は a の左側です。内積の節で求めたなす角 45度とも合います(√10 × √5 × sin45度 = 5)。

[2D外積] a×b = 5  b×a = -5
np.cross 3成分に0を足して: [0 0 5]
三角形の面積 = 5.5

3点 A(1, 1)・B(5, 2)・C(2, 4) の三角形の面積は、AB = (4, 1)、AC = (1, 3) の外積 4 × 3 − 1 × 1 = 11 の半分で 5.5 です。

画面の座標(y が下向き)では左右が逆に見える

数学の座標は y が上向きですが、ブラウザの Canvas やゲームの2D画面は、ふつう y が下向きです。同じ式でも、画面で見ると符号の意味が逆になります。

画面で右上の敵: 外積 = 2.0

画面の上向き (0, −1) を向いたキャラクターから見て、画面の右上 (2, −5) にいる敵の外積は 0 × (−5) − (−1) × 2 = 2 でプラスでした。数学の座標なら「左」の符号ですが、画面では右にいます。Godot の Vector2.cross のリファレンスも「2つ目のベクトルが1つ目から時計回りなら正」と書いており、y が下向きの画面座標での説明になっています。左右の判定を書くときは、座標の y がどちら向きかを先に確かめます。

NumPy の np.cross に2次元ベクトルを渡すと計算はできますが、NumPy 2.0 から非推奨の警告が出ます。

DeprecationWarning: Arrays of 2-dimensional vectors are deprecated. Use arrays of 3-dimensional vectors instead. (deprecated in NumPy 2.0)

np.linalg.cross は2次元を受け付けず、ValueError: Both input arrays must be (arrays of) 3-dimensional vectors, but they are 2 and 2 dimensional instead. になりました。2次元の外積は a[0]*b[1] - a[1]*b[0] と自分で書くのが確実です。

NumPyで内積・外積を計算する関数の違い

この節では、NumPy にある似た関数(dot・@・inner・vdot・vecdot・cross・outer)の違いと、たくさんのベクトルをまとめて計算する書き方・速さが分かります。

同じ a = (1, 2, 3)、b = (4, 5, 6) を渡した結果です。

書き方 結果 何をするか
a * b [ 4 10 18] 成分ごとの積(内積ではない)
np.dot(a, b)・a @ b 32 内積(2次元配列なら行列の積)
np.inner(a, b) 32 内積(最後の軸どうし)
np.vdot(a, b) 32 内積(複素数は1つ目を共役にする)
np.vecdot(a, b) 32 内積(NumPy 2.0 から。たくさんの組をまとめて)
np.einsum("i,i->", a, b) 32 添字で書く内積
np.cross(a, b)・np.linalg.cross(a, b) [−3 6 −3] 外積(クロス積)
np.outer(a, b) 3行3列の行列 直積(outer product)

NumPy のドキュメントは、np.dot について「1次元どうしならベクトルの内積(複素共役なし)」「2次元どうしなら行列の積で、matmul か a @ b が望ましい」と説明しています。複素数では np.dot([1+1j, 2], [1+1j, 2]) が (4+2j)、np.vdot が (6+0j) と違う値になりました。

たくさんのベクトルをまとめて計算する(速さの実測)

ゲームの全キャラクター、検索の全文書のように、何万組も計算するときは、1組ずつ Python のループで回さず、配列ごと渡します。

rng = np.random.default_rng(0)
V = rng.random((100_000, 3)); W = rng.random((100_000, 3))
np.vecdot(V, W)                 # 10万組の内積 → 長さ10万の配列
np.einsum("ij,ij->i", V, W)     # 同じ
np.sum(V * W, axis=1)           # 同じ
np.cross(V, W)                  # 10万組の外積 → 10万×3 の配列
vecdot           1.14 ms
sum(V*W,axis=1)  2.53 ms
einsum           0.74 ms
loop np.dot      179.64 ms
100万次元の内積: Python ループ 34.40 ms  np.dot 0.293 ms  比 118倍
10万組の外積: 1組ずつ np.cross 2819.3 ms  まとめて np.cross 2.17 ms  比 1297倍
まとめて計算した結果が一致: True
118倍100万次元の内積Python ループ 34.40ms → np.dot 0.293ms
1297倍10万組の外積1組ずつ 2819ms → まとめて 2.17ms
16ms1000件×384次元の全組の類似度正規化した行列 D @ D.T

10万組の外積は、1組ずつ np.cross を呼ぶと 2.8 秒、配列ごと渡すと 2.17 ミリ秒でした。 NumPy を使わない場合、Python 3.12 から入った math.sumprod(x, y) は、100万次元で sum(a*b for a, b in zip(x, y)) の 39.59 ms に対して 10.80 ms でした(Python 3.11 には無い関数です)。配列の作り方や形の変え方は、関連記事「NumPyの使い方 基本一覧|配列の作成・演算・形状変更」で扱っています。

プログラミングでの使い道(ゲームの向き判定・法線・コサイン類似度)

この節では、内積と外積がプログラムのどこで使われるかを、動くコードと出力で確かめられます。

プログラミングの中で数学がどの場面に出てくるかの全体像は、関連記事「プログラミングに数学は必要か|得意なら生きる場面と、実際に使う計算」で扱っています。

やりたいこと 使う計算 判定・計算のしかた
敵が前か後ろか 内積 向き・(敵 − 自分) がプラスなら前
敵が右か左か 2次元の外積 符号で左右(座標の y の向きに注意)
視野(扇形)に入るか 正規化した内積 cos(視野角 ÷ 2) 以上なら視野内
面の向き(法線) 外積 2辺の外積を正規化
裏向きの面を描かない 内積 視線・法線がプラスなら裏
光の当たり具合 内積 max(0, 法線・光の向き)
壁で跳ね返る 内積 d − 2(d・n)n
文書・画像の似ている度合い 内積(コサイン類似度) 正規化したベクトルの内積
線分が交わるか・点が内側か 2次元の外積 符号の組み合わせ
多角形の面積 2次元の外積の和 和の絶対値 ÷ 2

ゲーム:敵は前か後ろか、右か左か、視野に入るか

プレイヤーが (0, 0) にいて上 (0, 1) を向き、視野が90度のとき、5体の敵を判定しました(数学の座標で y が上向き)。

import math
import numpy as np

def cross2(u, v):
    return u[0] * v[1] - u[1] * v[0]

player = np.array([0.0, 0.0]); forward = np.array([0.0, 1.0])   # 向きは長さ1
cos_half = math.cos(math.radians(90 / 2))                        # 視野90度 → 片側45度
for name, pos in {"E1": (2, 5), "E2": (-3, 1), "E3": (1, -4), "E4": (4, 1), "E5": (0, 3)}.items():
    to = np.array(pos, dtype=float) - player
    d = forward @ to                       # 内積:前(+) / 後ろ(−)
    c = cross2(forward, to)                # 外積:左(+) / 右(−)
    cos = d / np.linalg.norm(to)
    print(name, pos, "前" if d > 0 else "後ろ", "左" if c > 0 else "右" if c < 0 else "線上",
          round(cos, 3), cos >= cos_half)
E1 (2, 5) 前 右 0.928 True
E2 (-3, 1) 前 左 0.316 False
E3 (1, -4) 後ろ 右 -0.97 False
E4 (4, 1) 前 右 0.243 False
E5 (0, 3) 前 線上 1.0 True
敵 内積(前後) 外積(左右) cos(視野90度は0.7071以上)
E1 (2, 5) 5(前) −2(右) 0.928(視野内)
E2 (−3, 1) 1(前) 3(左) 0.316(視野外)
E3 (1, −4) −4(後ろ) −1(右) −0.970(視野外)
E4 (4, 1) 1(前) −4(右) 0.243(視野外)
E5 (0, 3) 3(前) 0(正面の線上) 1.000(視野内)

5体の位置を図にしました(中央がプレイヤー、上が向いている方向)。

---
config:
  quadrantChart:
    chartWidth: 420
    chartHeight: 380
---
quadrantChart
    title 敵の位置(中央がプレイヤー、上が前)
    x-axis 左 外積がプラス --> 右 外積がマイナス
    y-axis 後ろ 内積がマイナス --> 前 内積がプラス
    quadrant-1 前で右
    quadrant-2 前で左
    quadrant-3 後ろで左
    quadrant-4 後ろで右
    E1: [0.667, 0.917]
    E2: [0.250, 0.583]
    E3: [0.583, 0.167]
    E4: [0.833, 0.583]
    E5: [0.500, 0.750]

E2 と E4 は内積がプラスで「前」ですが、cos が 0.316 と 0.243 で 0.7071 より小さいので、90度の視野の外でした。 角度を acos で出して比べるより、cos のまま比べるほうが計算が少なく、範囲外の誤差(後の節)も起きません。

3D:法線ベクトル・裏面の判定・光の当たり方

3Dの面(ポリゴン)の向きは、頂点 P0・P1・P2 から作る2辺の外積で求めます。頂点を並べる順番(巻き方)で、法線の向きが逆になります。

P0, P1, P2 = np.array([0., 0, 0]), np.array([1., 0, 0]), np.array([0., 1, 0])
n = np.cross(P1 - P0, P2 - P0)            # 反時計回り P0→P1→P2
n_rev = np.cross(P2 - P0, P1 - P0)        # 順番を逆に
for cam in (np.array([0., 0, 5]), np.array([0., 0, -5])):
    s = (P0 - cam) @ n                    # 視線・法線
    print(n, n_rev, cam, s, "表(描く)" if s < 0 else "裏(省く)")
[0. 0. 1.] [ 0.  0. -1.] [0. 0. 5.] -5.0 表(描く)
[0. 0. 1.] [ 0.  0. -1.] [ 0.  0. -5.] 5.0 裏(省く)

法線は (0, 0, 1) で、頂点の順を逆にすると (0, 0, −1) に裏返りました。カメラから面への視線と法線の内積がマイナスなら、面はカメラの方を向いています。プラスなら裏向きなので、描画を省けます(裏面カリング)。

光の明るさ(ランバート反射)は、単位法線 n と、面から光源への単位ベクトル L の内積 max(0, n・L) で決めるのが基本形です。

光の角度   0度: n·L =  1.000  明るさ max(0,n·L) = 1.000
光の角度  30度: n·L =  0.866  明るさ max(0,n·L) = 0.866
光の角度  60度: n·L =  0.500  明るさ max(0,n·L) = 0.500
光の角度  90度: n·L =  0.000  明るさ max(0,n·L) = 0.000
光の角度 120度: n·L = -0.500  明るさ max(0,n·L) = 0.000

真上からの光で 1.0、60度傾くと半分の 0.5、90度を超えると裏から当たるので 0 にします。進む向き d = (1, −1) のボールが床(法線 n = (0, 1))で跳ね返る向きは d − 2(d・n)n = (1, 1) になりました。

ゲームエンジンでの座標系の違い

外積の成分の計算式はどこでも同じですが、エンジンによって座標軸の取り方(右手系・左手系)が違うため、結果の向きを考える手が変わります。

エンジン 座標系と外積の向き(公式ドキュメントの説明)
Unity 6.6 Vector3.Cross の結果の向きは「左手の法則」で決まる
Unreal Engine 5.8 左手系。Z が上、X が前、Y が右
Godot Vector3.cross() は右手系で向きを返す。前(FORWARD)は (0, 0, −1)、上(UP)は (0, 1, 0)

内積は Unity が Vector3.Dot、Godot が dot() です。Unity の Vector3.Dot の説明には「正規化したベクトルなら、同じ向きで 1、反対向きで −1、垂直で 0」とあり、Godot の dot の説明にも「敵がプレイヤーの方を向いているかの判定に使える」という例が書かれています。UE5 の始め方は、関連記事「UE5(Unreal Engine 5)とは|始め方・必要スペック・料金を解説」で扱っています。

コサイン類似度(検索・AI)

コサイン類似度は、2つのベクトルの内積を、それぞれの大きさで割った値(=cosθ)です。 長さ(文書の長さ・単語の数)の違いを消して、向きの近さだけで比べます。

語の出現回数を [猫, 犬, 飼う, 株] の4次元で表し、検索語「猫 飼う」= (1, 0, 1, 0) と4つの文書を比べました。

q = np.array([1, 0, 1, 0])     # [猫, 犬, 飼う, 株]
for title, v in {"猫の飼い方": [3, 0, 1, 0], "犬の飼い方": [0, 3, 1, 0],
                 "猫と犬のしつけ": [2, 2, 1, 0], "株の買い方": [0, 0, 0, 4]}.items():
    v = np.array(v)
    print(title, q @ v, round(q @ v / (np.linalg.norm(q) * np.linalg.norm(v)), 4))
猫の飼い方 4 0.8944
犬の飼い方 1 0.2236
猫と犬のしつけ 3 0.7071
株の買い方 0 0.0

検索語「猫 飼う」とのコサイン類似度棒の長さは類似度(1を全幅)

猫の飼い方0.8944
猫と犬のしつけ0.7071
犬の飼い方0.2236
株の買い方0.0000

同じ文書を10回くり返したベクトルと、全ベクトルを長さ1に正規化してから行列の積で一度に計算した結果も出しました。

同じ文書を10回くり返し: 内積 4 → 40  cos 0.8944 → 0.8944
正規化後の 行列 @ 検索語 = [0.8944 0.2236 0.7071 0.    ]

同じ文書を10回くり返すと、内積は 4 から 40 に増えましたが、コサイン類似度は 0.8944 のままでした。 内積だけで比べると長い文書ほど有利になるので、大きさで割ります。先にすべてのベクトルを長さ1に正規化しておけば、行列の積1回で全文書との類似度が出ます(最後の行)。生成AIの埋め込み(エンベディング)でベクトル検索をするときも同じ計算です。埋め込みの作り方とベクトル検索は、関連記事「エンベディングとは?ベクトル検索とコサイン類似度を計算例で解説」で扱っています。Transformer の Attention も、Query と Key の内積で「どの単語に注目するか」を決めています。その計算は、関連記事「ニューラルネットワークとは|仕組み・学習・AttentionをNumPyで実演」で扱っています。

2D の当たり判定:線分の交差・点が三角形の内側か・多角形の面積

2次元の外積の符号を組み合わせると、図形の判定ができます。

import numpy as np

def cross2(u, v):
    return u[0] * v[1] - u[1] * v[0]

def intersect(p1, p2, p3, p4):
    """線分 p1p2 と p3p4 が交わるか(端で接する場合は含めない)"""
    d1 = cross2(p2 - p1, p3 - p1); d2 = cross2(p2 - p1, p4 - p1)
    d3 = cross2(p4 - p3, p1 - p3); d4 = cross2(p4 - p3, p2 - p3)
    return bool(d1 * d2 < 0 and d3 * d4 < 0), [int(d1), int(d2), int(d3), int(d4)]

def inside(tri, p):
    """点 p が三角形 tri の内側か(3辺との外積の符号がそろうか)"""
    s = [cross2(tri[(i + 1) % 3] - tri[i], p - tri[i]) for i in range(3)]
    return all(x > 0 for x in s) or all(x < 0 for x in s), [float(x) for x in s]

def area(poly):
    """多角形の面積(靴ひもの公式)"""
    s = sum(cross2(poly[i], poly[(i + 1) % len(poly)]) for i in range(len(poly)))
    return abs(s) / 2, "反時計回り" if s > 0 else "時計回り"

P = lambda *xy: np.array(xy)
print("AB と CD", intersect(P(0, 0), P(4, 4), P(0, 4), P(4, 0)))
print("AB と EF", intersect(P(0, 0), P(4, 4), P(5, 1), P(6, 0)))
tri = [P(0, 0), P(4, 0), P(0, 4)]
print("(1,1)", inside(tri, P(1, 1)), " (3,3)", inside(tri, P(3, 3)))
print("五角形", area([P(0, 0), P(4, 0), P(4, 3), P(2, 5), P(0, 3)]))
AB と CD (True, [16, -16, -16, 16])
AB と EF (False, [-16, -24, -6, 2])
(1,1) (True, [4.0, 8.0, 4.0])  (3,3) (False, [12.0, -8.0, 12.0])
五角形 (np.float64(16.0), '反時計回り')
  • 線分の交差:A(0,0)–B(4,4) と C(0,4)–D(4,0) は、互いに相手の両端が反対側(符号が逆)にあるので交わります。E(5,1)–F(6,0) とは、C・D 側の符号がそろっている(−16 と −24)ので交わりません。
  • 点が三角形の内側か:三角形 (0,0)・(4,0)・(0,4) の3辺それぞれと点の外積が全部同じ符号なら内側です。(1, 1) は 4・8・4 で内側、(3, 3) は −8 が混じるので外側でした。
  • 多角形の面積:頂点を順に (0,0)・(4,0)・(4,3)・(2,5)・(0,3) とすると、となり合う頂点どうしの外積の和は 32(実行して確かめた値)、面積は 16 です(長方形 12 + 三角形 4)。和がプラスなら頂点は反時計回りに並んでいます。この方法は「靴ひもの公式(座標法)」とも呼ばれます。

JavaScript で書く場合

ブラウザのゲームや Canvas で使うときの最小の関数です。Node.js 22.22.2 で実行しました。

const dot = (a, b) => a.reduce((s, v, i) => s + v * b[i], 0);
const cross = (a, b) => [a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]];
const cross2 = (a, b) => a[0]*b[1] - a[1]*b[0];
const norm = (a) => Math.hypot(...a);
const a = [1, 2, 3], b = [4, 5, 6];
console.log("dot", dot(a, b), "cross", cross(a, b), "|a×b|", norm(cross(a, b)));
const cos = dot([3,1],[1,2]) / (norm([3,1]) * norm([1,2]));
// 範囲を -1〜1 に収めてから acos
console.log("cos", cos, "deg", Math.acos(Math.min(1, Math.max(-1, cos))) * 180 / Math.PI);
console.log("cross2", cross2([3,1],[1,2]), "Math.acos(1.0000000000000002) =", Math.acos(1.0000000000000002));
dot 32 cross [ -3, 6, -3 ] |a×b| 7.348469228349534
cos 0.7071067811865475 deg 45.00000000000001
cross2 5 Math.acos(1.0000000000000002) = NaN

JavaScript の Math.acos は、1 をわずかに超えた値で例外ではなく NaN を返しました。次の節の Python の結果と比べると違いが分かります。

内積・外積の練習問題(答えつき)

この節では、ここまでの公式を使う4問で理解を確かめられます。答えは NumPy でも確かめた値です。

問題 答え
1. a = (1, 0, 1)、b = (0, 1, 1) のなす角は? a・b = 1、大きさはどちらも √2。cosθ = 1/2 で 60度
2. 上の a と b の両方に垂直な単位ベクトルは? a×b = (−1, −1, 1)。長さ √3 で割り ±(−1, −1, 1)/√3
3. 3点 (1,0,0)・(0,2,0)・(0,0,3) の三角形の面積は? 2辺の外積 (6, 3, 2) の長さ 7 の半分で 3.5
4. 大きさ2の a・大きさ1の b で a・b = 1 のとき、a + 2b の大きさは? 4 + 4×1 + 4×1 = 12 の平方根で 2√3 ≒ 3.464
練習1 a·b = 1  cos = 0.4999999999999999  θ = 60.00000000000001
練習2 a×b = [-1 -1  1]  単位 = [-0.57735027 -0.57735027  0.57735027]  1/√3 = 0.5773502691896258
練習3 面積 = 3.5
練習4 |a|,|b|,a·b = 2.0 0.9999999999999999 1.0  |a+2b| = 3.4641016151377544  2√3 = 3.4641016151377544

問題4は「大きさの2乗を、内積の展開で計算する」形です。大きさ(a + 2b)² = a・a + 4a・b + 4b・b = 4 + 4 + 4 = 12 と展開します。両方に垂直な単位ベクトルは、向きが逆のものも答えになるので、2つあります。

内積・外積のよくある間違いとエラーの直し方

この節では、内積・外積の計算で起こりやすい考え違いと、プログラムで出るエラー・警告の原文と直し方が分かります。

  • 1内積をベクトルだと思っていないか内積の答えは数。成分ごとの積 a * b は [4 10 18] で内積ではない
  • 2外積の順番を入れかえていないかb×a = −(a×b)。法線が裏返り、面が消えたり光が当たらなくなる
  • 3y 成分の符号を間違えていないかy は a3b1 − a1b3(z と x の順)。最後に a・(a×b) = 0 で検算
  • 4acos に渡す前に −1〜1 に収めたか同じ向きでも cos が 1.0000000000000002 になり、math.acos がエラー
  • 5小数の内積を == 0 で比べていないか垂直のはずが −1.66e−18。np.isclose か小さな許容値で比べる
  • 6零ベクトルで割っていないか長さ0のベクトルは角度が決まらず、cos が nan になる
  • 7座標の y の向きと右手系・左手系を確かめたか画面座標や左手系のエンジンでは、左右・表裏の符号の読み方が変わる
  • 8小さな整数型で計算していないかint8 の [100, 100] どうしの内積は 20000 ではなく 32 になった(警告なし)

cos が 1 をわずかに超えて acos でエラーになる

同じ向きのベクトル(x と 2.5x)でも、丸め誤差で cos が 1 を超えることがあります。

import math
import numpy as np

rng = np.random.default_rng(1)
for _ in range(100_000):                      # 同じ向きで長さだけ違う2つのベクトルを試す
    x = rng.random(3); y = x * 2.5
    c = x @ y / (np.linalg.norm(x) * np.linalg.norm(y))
    if c > 1:
        break
print(x, repr(c))
print(np.arccos(np.clip(c, -1.0, 1.0)))       # 範囲に収めてから
print(np.arccos(c))                           # 警告つきで nan
print(math.acos(c))                           # ValueError
[0.94864945 0.31183145 0.42332645] np.float64(1.0000000000000002)
0.0
RuntimeWarning: invalid value encountered in arccos
nan
ValueError: math domain error

警告とエラーは、要点の1行だけを載せています。np.clip(cos, -1.0, 1.0) で範囲に収めてから arccos に渡すと、0.0(0度)が返りました。 nan は後の比較をすべて False にするので、気づかないまま判定がおかしくなります。

直交のはずなのに 0 にならない

dot [0.1, 0.3, 0.2] [0.3, -0.1, 0.0] np.float64(-1.6653345369377347e-18) False True
dot [0.7, 0.1, 0.3] [0.1, -0.1, -0.2] np.float64(-1.0269562977782698e-17) False True

手計算では 0.03 − 0.03 = 0 ですが、== 0 は False、np.isclose(d, 0) は True でした。0.1 などの小数は2進数でぴったり表せないためです。外積で「平行か」を判定するときも同じで、長さが小さい値(例:1e−9 × 大きさの積)以下かで比べます。

エラー・警告の原文と直し方

どれも NumPy 2.4.6・Python 3.11.15 で実際に出たものです。

エラー・警告 原因 直し方
ValueError: shapes (3,) and (2,) not aligned: 3 (dim 0) != 2 (dim 0) np.dot に長さの違うベクトル 次元をそろえる
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, … @ に長さの違うベクトル 次元をそろえる
ValueError: shapes (1,3) and (1,3) not aligned: 3 (dim 1) != 1 (dim 0) 1行3列どうしを np.dot 1次元にするか片方を .T(結果は [[32]])
ValueError: incompatible dimensions for cross product np.cross に4次元ベクトル 外積は3次元。2次元は自分で式を書く
DeprecationWarning: Arrays of 2-dimensional vectors are deprecated. … np.cross に2次元ベクトル a[0]*b[1] - a[1]*b[0] か z=0 を足す
ValueError: Both input arrays must be (arrays of) 3-dimensional vectors, … np.linalg.cross に2次元 3次元にする
TypeError: can't multiply sequence by non-int of type 'list' Python のリストどうしを * np.array にするか zip で掛けて足す
ValueError: math domain error math.acos に 1 を超える値 max(-1.0, min(1.0, c)) で収める
RuntimeWarning: invalid value encountered in scalar divide 零ベクトルの大きさ 0 で割った 長さ0を先に除く

np.cross の4次元のエラーは、実際には2行で ValueError: incompatible dimensions for cross product と (dimension must be 2 or 3) が出ます。2次元をまだ受け付けると読めますが、2次元は上の警告のとおり非推奨です。

よくある質問

Q. 内積とは簡単に言うと何ですか? A. 2つのベクトルの「同じ向きの成分どうしを掛けて足した数」で、向きが近いほど大きく、垂直で0、反対向きでマイナスになります。式は a1b1 + a2b2 + a3b3、または大きさの積 × cosθ です。

Q. 内積と外積の違いは何ですか? A. 内積の答えは数で、角度・直交・射影が分かります。外積の答えはベクトルで、2つに垂直な向きと平行四辺形の面積が分かります。内積は順番を入れかえても同じ、外積は向きが逆になります。

Q. 内積が0になるのはどんなときですか? A. 2つのベクトルが垂直(直交)のときと、どちらかが零ベクトルのときです。(2, 1) と (−1, 2) は 2 × (−1) + 1 × 2 = 0 で垂直です。

Q. 内積がマイナスになるのはどういう意味ですか? A. なす角が90度より大きい(鈍角)、つまり2つがだいたい反対向きという意味です。ゲームでは「敵が後ろにいる」判定に使えます。

Q. 外積の公式の覚え方は? A. x 成分は「y と z」、y 成分は「z と x」、z 成分は「x と y」を、たすき掛けして引きます(x → y → z → x の輪の順)。計算後に a・(a×b) = 0 を確かめると、符号の間違いに気づけます。

Q. 外積の向きはどう決まりますか? A. 右手の親指を a、人差し指を b に向けたときの中指の向き(右ねじを a から b へ回して進む向き)です。Unity の Vector3.Cross は左手の法則で向きを説明しており、使う座標系で手が変わります。

Q. 2次元ベクトルの外積はどう計算しますか? A. a1b2 − a2b1 という1つの数として使います。プラスなら b は a の左側(反時計回り)、マイナスなら右側、0 なら平行で、絶対値は平行四辺形の面積です。画面座標(y が下向き)では左右が逆に見えます。

Q. 外積で三角形の面積を求めるには? A. 1つの頂点から他の2つの頂点へのベクトルの外積を求め、その長さを2で割ります。(1,0,0)・(0,2,0)・(0,0,3) の三角形は、外積 (6, 3, 2) の長さ 7 の半分で 3.5 でした。

Q. 4次元以上のベクトルにも外積はありますか? A. この記事の形の外積(2つのベクトルから垂直なベクトルを作る)は3次元(と7次元)でしか定義できません。NumPy の np.cross に4次元を渡すと ValueError: incompatible dimensions for cross product になりました。内積は何次元でも計算できます。

Q. NumPy で内積・外積を計算するには? A. 内積は a @ b か np.dot(a, b)、外積は np.cross(a, b) です。たくさんの組をまとめるなら np.vecdot(V, W) と np.cross(V, W) に配列ごと渡します。np.outer は直積(行列)で、クロス積ではありません。

Q. 内積とコサイン類似度の関係は? A. コサイン類似度は、内積を2つのベクトルの大きさで割った値(cosθ)です。長さの違いを消して向きだけで比べるので、文書を10回くり返しても値は 0.8944 のままでした。

Q. 外積は英語で何と言いますか? A. cross product(クロス積)または vector product(ベクトル積)です。内積は dot product・inner product・scalar product です。outer product は直積と呼ばれる別の計算です。

まとめ

  • 内積は2つのベクトルから数を作り、a1b1 + a2b2 + a3b3 = 大きさの積 × cosθ。外積は両方に垂直なベクトルを作り、(a2b3 − a3b2, a3b1 − a1b3, a1b2 − a2b1)、長さは大きさの積 × sinθ。
  • a = (1, 2, 3)、b = (4, 5, 6) で、手計算の内積 32・外積 (−3, 6, −3)・面積 √54 が NumPy 2.4.6 と一致した。
  • 内積でわかること:符号で鋭角・鈍角、0 で垂直、a・b ÷ |a| で射影の長さ。
  • 外積でわかること:法線(2つに垂直な向き)、平行四辺形・三角形の面積、平行なら零ベクトル、三重積で体積。向きは右手系(エンジンによって左手系)。
  • 2次元の外積 a1b2 − a2b1 は符号で左右・回る向きが分かる。画面座標では左右が逆に見える。
  • ゲームの前後・左右・視野、法線と裏面カリング、光の当たり方、コサイン類似度、当たり判定は、どれも内積と外積の組み合わせ。
  • acos の前に −1〜1 に収める、小数は許容値で比べる、外積の順番と座標系を確かめる。

参考・出典

確認日はいずれも 2026年10月22日です。

  • NumPy「numpy.dot」:https://numpy.org/doc/stable/reference/generated/numpy.dot.html
  • NumPy「numpy.cross」:https://numpy.org/doc/stable/reference/generated/numpy.cross.html
  • NumPy「numpy.linalg.cross」:https://numpy.org/doc/stable/reference/generated/numpy.linalg.cross.html
  • NumPy「numpy.vecdot」:https://numpy.org/doc/stable/reference/generated/numpy.vecdot.html
  • Wikipedia「ドット積」:https://ja.wikipedia.org/wiki/ドット積
  • Wikipedia「クロス積」:https://ja.wikipedia.org/wiki/クロス積
  • Unity Scripting API「Vector3.Cross」:https://docs.unity3d.com/ScriptReference/Vector3.Cross.html
  • Unity Scripting API「Vector3.Dot」:https://docs.unity3d.com/ScriptReference/Vector3.Dot.html
  • Unreal Engine 5.8「Coordinate System and Spaces」:https://dev.epicgames.com/documentation/en-us/unreal-engine/coordinate-system-and-spaces-in-unreal-engine
  • Godot Engine「Vector2.xml・Vector3.xml(クラスリファレンス)」:https://github.com/godotengine/godot/tree/master/doc/classes

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「内積・外積とは|公式と計算例・使い道をNumPyで実演」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?