- 技術 💻
内積・外積とは|公式と計算例・使い道をNumPyで実演
結論
内積は2つのベクトルから「数」を、外積は両方に垂直な「ベクトル」を作る計算です。内積は cos、外積は sin と結びつきます。
この記事でわかること
- 公式4つの手計算とNumPyでの一致確認
- 角度・直交・射影・面積・左右判定の5つの使い方
- ゲーム・法線・類似度のコードと間違い8種
同じ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) で計算すると
内積は「数」、外積は「ベクトル」。ここがいちばん大きな違い
| 項目 | 内積 | 外積 |
|---|---|---|
| 英語 | 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)
成分どうしを掛けて足す
a・b = 3 × 1 + 1 × 2 = 3 + 2 = 5。
大きさを出す
|a| = √(9 + 1) = √10、|b| = √(1 + 4) = √5。
cosθ を出す
cosθ = 5 ÷ (√10 × √5) = 5 ÷ √50 = 5 ÷ (5√2) = 1 ÷ √2。
角度にする
cosθ = 1/√2 になる角度は 45度。a と b のなす角は 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」の順に、たすき掛けして引きます。
2行に並べる
上の行に a の成分 1・2・3、下の行に b の成分 4・5・6 を書きます。
x 成分:x の列を隠す
残った y・z の列で、左上×右下 − 右上×左下。2 × 6 − 3 × 5 = 12 − 15 = −3。
y 成分:z と x の順で
z・x の順に並べて同じように。3 × 4 − 1 × 6 = 12 − 6 = 6。x・z の順で計算するときは符号を逆にします。
z 成分:z の列を隠す
残った x・y の列で、1 × 5 − 2 × 4 = 5 − 8 = −3。
内積で検算する
(−3, 6, −3)・a = −3 + 12 − 9 = 0、(−3, 6, −3)・b = −12 + 30 − 18 = 0。両方0なら垂直で、計算は合っています。
覚え方のこつは、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 のどちらとの内積も 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
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
同じ文書を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期連続の黒字決算。