#11 ふれてみよう高校数学 幾何と図形

ベクトルの内積

内積とは

「懐中電灯の光を壁に当てるとき、光が壁に垂直なら影は点になり、斜めになるほど影が長くなる」——この「どのくらい同じ方向を向いているか」を数値で表すのが内積(dot product)です。

2 つのベクトルを「掛け合わせる」方法のひとつが内積で、結果はスカラー(向きを持たない数値)になります——「2 つの矢印から 1 つの数を作る演算」と覚えましょう。

定義:角度で定義

「2 つのベクトルのなす角が小さいほど(同じ方向を向いているほど)、内積は大きくなる」——これが内積の本質です:

ab=abcosθ\vec{a} \cdot \vec{b} = |\vec{a}||\vec{b}|\cos\theta

ここで θ\theta は 2 つのベクトルのなす角(0θπ0 \leq \theta \leq \pi)。cos0°=1\cos 0° = 1(同じ向き)、cos90°=0\cos 90° = 0(直角)、cos180°=1\cos 180° = -1(逆向き)。

定義:成分で定義

「横成分どうし、縦成分どうしをかけて足す」——計算はシンプルです。

a=(a1,a2)\vec{a} = (a_1, a_2)b=(b1,b2)\vec{b} = (b_1, b_2) のとき:

ab=a1b1+a2b2\vec{a} \cdot \vec{b} = a_1 b_1 + a_2 b_2

この 2 つの定義は等価です——角度の定義と成分の定義は同じ値を与えます。

内積の幾何的意味

b\vec{b}a\vec{a} の方向に”影”として投影したとき、その影の長さと a\vec{a} の大きさをかけた値」——これが内積の幾何的なイメージです:

ab=a×(b の a 方向への射影)\vec{a} \cdot \vec{b} = |\vec{a}| \times (\vec{b} \text{ の } \vec{a} \text{ 方向への射影})

つまり「a\vec{a} の大きさ × b\vec{b}a\vec{a} への投影の長さ」。

  • θ<90°\theta < 90°:内積 > 0(同じ方向寄り)——「同じ方向を向いている」
  • θ=90°\theta = 90°:内積 = 0(直交)——「完全に直角」
  • θ>90°\theta > 90°:内積 < 0(逆方向寄り)——「反対方向を向いている」

直交条件

「2 つのベクトルが垂直かどうかを調べるのに、分度器は不要」——内積がゼロかどうかだけ確認すれば OK です:

a0\vec{a} \neq \vec{0}b0\vec{b} \neq \vec{0} のとき:

ab    ab=0\vec{a} \perp \vec{b} \iff \vec{a} \cdot \vec{b} = 0

これは非常によく使います。証明問題で「ab=0\vec{a} \cdot \vec{b} = 0 を示せ」という形が頻出です——「成分を代入して計算するだけ」で垂直性が証明できます。

インタラクティブ図解:内積と角度

マウスを動かすとベクトル b\vec{b} が回転します。内積の値と角度がリアルタイムに更新されます。直交したとき(内積 = 0)に特別なハイライトが出ます。

マウスを動かして b⃗ を回転させる。内積が 0 になるとき(直交・垂直)に画面の変化に注目

var OX = 300, OY = 180, SC = 60;

function arrow(x1, y1, x2, y2, color, width) {
var dx = x2-x1, dy = y2-y1;
var len = Math.sqrt(dx*dx+dy*dy);
if (len < 2) return;
var ux=dx/len, uy=dy/len;
var hw=9, hl=13;
ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2-ux*hl,y2-uy*hl);
ctx.strokeStyle=color; ctx.lineWidth=width; ctx.stroke();
ctx.beginPath(); ctx.moveTo(x2,y2);
ctx.lineTo(x2-ux*hl-uy*hw, y2-uy*hl+ux*hw);
ctx.lineTo(x2-ux*hl+uy*hw, y2-uy*hl-ux*hw);
ctx.closePath(); ctx.fillStyle=color; ctx.fill();
}

function loop() {
ctx.clearRect(0, 0, W, H);

// グリッド
ctx.strokeStyle = '#161b22'; ctx.lineWidth = 1;
for (var gx=-4;gx<=6;gx++) {ctx.beginPath();ctx.moveTo(OX+gx*SC,0);ctx.lineTo(OX+gx*SC,H);ctx.stroke();}
for (var gy=-3;gy<=4;gy++) {ctx.beginPath();ctx.moveTo(0,OY-gy*SC);ctx.lineTo(W,OY-gy*SC);ctx.stroke();}
ctx.strokeStyle='#30363d';ctx.lineWidth=1.5;
ctx.beginPath();ctx.moveTo(0,OY);ctx.lineTo(W,OY);ctx.stroke();
ctx.beginPath();ctx.moveTo(OX,0);ctx.lineTo(OX,H);ctx.stroke();
ctx.fillStyle='#8b949e';ctx.font='12px sans-serif';
ctx.fillText('x',W-14,OY-8);ctx.fillText('y',OX+6,14);

// a = (2, 1)(固定)
var ax=2, ay=1;
var aLen = Math.sqrt(ax*ax+ay*ay);

// b = マウス方向の単位ベクトル × 2.2
var dx = mx - OX, dy = -(my - OY);
var dLen = Math.sqrt(dx*dx+dy*dy);
var bLen = 2.2;
var bx = dLen > 0.1 ? (dx/dLen)*bLen : 0;
var by = dLen > 0.1 ? (dy/dLen)*bLen : bLen;

var dot = ax*bx + ay*by;
var cosTheta = dot / (aLen * bLen);
cosTheta = Math.max(-1, Math.min(1, cosTheta));
var theta = Math.acos(cosTheta);
var thetaDeg = (theta * 180 / Math.PI);

// a の画面先端
var aEx = OX + ax*SC, aEy = OY - ay*SC;
var bEx = OX + bx*SC, bEy = OY - by*SC;

// 直交に近いかどうか
var isPerp = Math.abs(dot) < 0.15;

// 角度弧
var aAngle = Math.atan2(-ay, ax);
var bAngle = Math.atan2(-by, bx);
ctx.beginPath();
ctx.arc(OX, OY, 30, Math.min(aAngle,bAngle), Math.max(aAngle,bAngle));
ctx.strokeStyle = '#ffa65780'; ctx.lineWidth = 2; ctx.stroke();
ctx.fillStyle = '#ffa657'; ctx.font = '12px serif';
var midAngle = (aAngle + bAngle) / 2;
ctx.fillText('θ', OX + 38*Math.cos(midAngle), OY + 38*Math.sin(midAngle));

// b の a への射影
var projLen = dot / aLen;
var projX = OX + (ax/aLen) * projLen * SC;
var projY = OY - (ay/aLen) * projLen * SC;
ctx.beginPath(); ctx.moveTo(bEx, bEy); ctx.lineTo(projX, projY);
ctx.strokeStyle = '#f0883e50'; ctx.lineWidth = 1.5; ctx.setLineDash([3,3]); ctx.stroke(); ctx.setLineDash([]);
ctx.beginPath(); ctx.arc(projX, projY, 4, 0, Math.PI*2);
ctx.fillStyle = '#f0883e80'; ctx.fill();

// ベクトル a
arrow(OX, OY, aEx, aEy, isPerp ? '#ffa657' : '#58a6ff', 3);
ctx.fillStyle = isPerp ? '#ffa657' : '#58a6ff';
ctx.font = 'bold 14px serif';
ctx.fillText('a⃗', aEx+8, aEy-8);

// ベクトル b
arrow(OX, OY, bEx, bEy, isPerp ? '#ffa657' : '#f0883e', 3);
ctx.fillStyle = isPerp ? '#ffa657' : '#f0883e';
ctx.fillText('b⃗', bEx+8, bEy-8);

// 直交マーク
if (isPerp) {
  var pu = ax/aLen * 14, pv = ay/aLen * 14;
  var bu = bx/bLen * 14, bv = by/bLen * 14;
  ctx.beginPath();
  ctx.moveTo(OX+pu, OY-pv);
  ctx.lineTo(OX+pu+bu, OY-pv-bv);
  ctx.lineTo(OX+bu, OY-bv);
  ctx.strokeStyle = '#ffa657'; ctx.lineWidth = 2; ctx.stroke();
}

// 情報パネル
var pColor = isPerp ? '#ffa657' : (dot > 0 ? '#56d364' : '#f85149');
ctx.fillStyle = '#0d1117e0';
ctx.fillRect(8, 8, 260, 130);
ctx.strokeStyle = isPerp ? '#ffa657' : '#30363d'; ctx.lineWidth = isPerp ? 2 : 1;
ctx.strokeRect(8, 8, 260, 130);

ctx.font = '13px monospace';
ctx.fillStyle = '#58a6ff';
ctx.fillText('a⃗ = (' + ax + ', ' + ay + ')  |a⃗|=' + aLen.toFixed(2), 16, 28);
ctx.fillStyle = '#f0883e';
ctx.fillText('b⃗ = (' + bx.toFixed(2) + ', ' + by.toFixed(2) + ')  |b⃗|=' + bLen.toFixed(2), 16, 48);
ctx.fillStyle = '#ffa657';
ctx.fillText('θ = ' + thetaDeg.toFixed(1) + '°', 16, 68);
ctx.fillStyle = pColor;
ctx.font = 'bold 14px monospace';
ctx.fillText('a⃗·b⃗ = ' + dot.toFixed(3), 16, 90);
ctx.font = '12px monospace';
ctx.fillStyle = '#8b949e';
ctx.fillText('= |a||b|cosθ = ' + (aLen*bLen*cosTheta).toFixed(3), 16, 108);
if (isPerp) {
  ctx.fillStyle = '#ffa657'; ctx.font = 'bold 13px sans-serif';
  ctx.fillText('⊥ 直交!(dot = 0)', 16, 128);
} else {
  ctx.fillStyle = pColor; ctx.font = '12px sans-serif';
  ctx.fillText(dot > 0 ? '同方向寄り(鋭角)' : '逆方向寄り(鈍角)', 16, 128);
}

requestAnimationFrame(loop);
}
loop();

内積の性質

内積は次の性質を満たします(スカラー積の公理)——「普通の掛け算と似た性質が成り立つ」と覚えましょう:

ab=ba(交換律)\vec{a} \cdot \vec{b} = \vec{b} \cdot \vec{a} \quad \text{(交換律)} a(b+c)=ab+ac(分配律)\vec{a} \cdot (\vec{b} + \vec{c}) = \vec{a} \cdot \vec{b} + \vec{a} \cdot \vec{c} \quad \text{(分配律)} (ka)b=k(ab)(k\vec{a}) \cdot \vec{b} = k(\vec{a} \cdot \vec{b}) aa=a2\vec{a} \cdot \vec{a} = |\vec{a}|^2

最後の aa=a2\vec{a} \cdot \vec{a} = |\vec{a}|^2 は「自分自身との内積はベクトルの大きさの二乗」——θ=0°\theta = 0° なので cos0°=1\cos 0° = 1 となり、自然にこの形になります。

展開公式

(a+b)2=a2+2ab+b2(a+b)^2 = a^2 + 2ab + b^2 のベクトル版」——同じパターンで展開できます:

a+b2=a2+2ab+b2|\vec{a} + \vec{b}|^2 = |\vec{a}|^2 + 2\vec{a}\cdot\vec{b} + |\vec{b}|^2 ab2=a22ab+b2|\vec{a} - \vec{b}|^2 = |\vec{a}|^2 - 2\vec{a}\cdot\vec{b} + |\vec{b}|^2

これを使うと余弦定理がすっきり導けます——「余弦定理はベクトルの展開公式の別表現」です:

AB2=ba2=a22ab+b2|\overrightarrow{AB}|^2 = |\vec{b} - \vec{a}|^2 = |\vec{a}|^2 - 2\vec{a}\cdot\vec{b} + |\vec{b}|^2

余弦定理 c2=a2+b22abcosCc^2 = a^2 + b^2 - 2ab\cos C と対応します。

内積の応用

角度を求める

「2 本の矢印の間の角度を、成分だけで計算できる」——実用的な公式です:

cosθ=abab\cos\theta = \frac{\vec{a}\cdot\vec{b}}{|\vec{a}||\vec{b}|}

2 ベクトルの成分がわかれば、角度を求めることができます——「内積を計算して両辺の大きさで割るだけ」。

射影(projection)

「斜面を歩くとき、水平方向にどれだけ進んだか」——これが射影のイメージです。b\vec{b}a\vec{a} への射影の長さ:

proj=aba\text{proj} = \frac{\vec{a}\cdot\vec{b}}{|\vec{a}|}

射影ベクトル(a\vec{a} 方向への射影を矢印として表したもの):

p=aba2a\vec{p} = \frac{\vec{a}\cdot\vec{b}}{|\vec{a}|^2}\vec{a}

例題

a=(3,4)\vec{a} = (3, 4)b=(1,2)\vec{b} = (1, -2) のとき、

  1. ab\vec{a} \cdot \vec{b} を求めよ
  2. a\vec{a}b\vec{b} のなす角を求めよ
  3. ab\vec{a} \perp \vec{b} となるような kk を求めよ(b=(1,k)\vec{b} = (1, k) とする)

解:

  1. 成分ごとにかけて足す——ab=3×1+4×(2)=38=5\vec{a} \cdot \vec{b} = 3 \times 1 + 4 \times (-2) = 3 - 8 = -5

  2. a=5|\vec{a}| = 5b=5|\vec{b}| = \sqrt{5}——「3-4-5の直角三角形」から a=5|\vec{a}|=5 がすぐ分かります:

cosθ=555=15θ=arccos(15)116.6°\cos\theta = \frac{-5}{5\sqrt{5}} = \frac{-1}{\sqrt{5}} \Rightarrow \theta = \arccos\left(-\frac{1}{\sqrt{5}}\right) \approx 116.6°
  1. 直交条件「内積 = 0」を使う——ab=3+4k=0k=34\vec{a} \cdot \vec{b} = 3 + 4k = 0 \Rightarrow k = -\frac{3}{4}

まとめ

  • 内積の定義:ab=abcosθ=a1b1+a2b2\vec{a}\cdot\vec{b} = |\vec{a}||\vec{b}|\cos\theta = a_1b_1 + a_2b_2(2通りの定義が等価)——「角度版と成分版は同じ値」
  • 内積はスカラー(数値)を返す——「2 つの矢印から 1 つの数を取り出す演算」
  • 直交条件:ab=0\vec{a}\cdot\vec{b} = 0(最重要!)——「垂直かどうかは内積がゼロかで確認する」
  • 内積から角度・射影・余弦定理などが導ける——「内積は幾何と代数をつなぐ橋」

次回は空間(3 次元)へ飛び出してベクトルを扱います。