見出し画像

高校数学をプログラミングで解く(数学B編)「1-2 ベクトルの内積」


はじめに

今回は、数学Bで学ぶ「ベクトルの内積」について、ベクトルの内積を利用する問題を、ベクトルの成分を用いて内積を計算する方法とプログラミング言語 Processing で用意された内積を計算する関数を用いる方法の2つの方法でプログラムを作成して解いてみます。

ベクトルの内積

まず、ベクトルの内積についてその性質も含めてまとめておきます。なお、以下で$${k}$$は実数とし、$${\vec{a} = (a_1, a_2), \ \vec{b} = (b_1, b_2), \ \vec{a} \neq \vec{0}, \ \vec{b} \neq \vec{0}}$$とします。また、$${\vec{a}}$$と$${\vec{b}}$$のなす角を$${ \theta \ ( 0^{\circ} \leq \theta \leq 180^{\circ} ) }$$とします。

内積と成分

① 定義

$$
\vec{a} \cdot \vec{b} = | \vec{a} || \vec{b} | \cos \theta
$$

② 成分表示

$$
\vec{a} \cdot \vec{b} = a_1 b_1 + a_2 b_2
$$

③ 大きさ

$$
| \vec{a} |^2 = \vec{a} \cdot \vec{a} = a_1^2+a_2^2
$$

④ なす角

$$
\cos \theta = \frac{ \vec{a} \cdot \vec{b} }{ | \vec{a} || \vec{b} | } = \frac{ a_1b_1+a_2b_2}{ \sqrt{ a_1^2+a_2^2 } \sqrt{ b_1^2+b_2^2 }}
$$

⑤ 垂直条件

$$
\vec{a} \perp \vec{b} \Leftrightarrow \vec{a} \cdot \vec{b} = 0 \Leftrightarrow a_1b_1 + a_2b_2 = 0
$$

⑥ 平行条件

$$
\vec{a}  /\!\!/  \vec{b} \Leftrightarrow \vec{a} \cdot \vec{b} = \pm | \vec{a} || \vec{b} | \Leftrightarrow a_1b_2 - a_2b_1 = 0
$$

内積の性質

① $${ \vec{a} \cdot \vec{b} = \vec{b} \cdot \vec{a} }$$
② $${ \vec{a} \cdot \vec{a} = | \vec{a} |^2 }$$
③ $${ (k \vec{a}) \cdot \vec{b} = \vec{a} \cdot (k \vec{b}) = k ( \vec{a} \cdot \vec{b} ) }$$
④ $${ ( \vec{a} + \vec{b} ) \cdot \vec{c} = \vec{a} \cdot \vec{c} + \vec{b} \cdot \vec{c} }$$
⑤ $${ \vec{a} \cdot ( \vec{b} + \vec{c} ) = \vec{a} \cdot \vec{b} + \vec{a} \cdot \vec{c} }$$

ベクトルの内積に関する問題

今回は、下記の問題を解くプログラムを作成していきます。

問題
次の2つのベクトル$${ \vec{a}, \vec{b} }$$のそれぞれの大きさと内積を計算し、そのなす角を求めよ。
(1) $${ \vec{a} = ( \sqrt{3}, 3 ), \ \vec{b} = ( 3, \sqrt{3} ) }$$
(2) $${ \vec{a} = ( 3, 6 ), \ \vec{b} = ( 2, -6 ) }$$
(3) $${ \vec{a} = ( 2, 1 ), \ \vec{b} = ( 3, -6 ) }$$
(4) $${ \vec{a} = ( 1, 1 ), \ \vec{b} = ( -1, 2+\sqrt{3} ) }$$

 

ベクトルの成分を用いて内積を計算するプログラム

まず、ベクトルの大きさや内積をベクトルの成分を用いて計算して、それらの値をもとに2つのベクトルのなす角を求めるプログラムを作成します。

PVectorによるベクトル成分の取得

ベクトルの大きさや内積をベクトルの成分を用いて計算するために、まず、PVectorクラスを用いて生成したベクトルから$${x}$$成分、$${y}$$成分を取得する方法について解説します。といっても、そんなに難しいことではありません。

PVector a = new PVector(a1,a2); // ベクトルの生成
a.x; // ベクトルaのx成分
a.y; // ベクトルaのy成分

上記のように、PVectorクラスとして生成したベクトル a に対して、
a.x : ベクトルaの$${x}$$成分 float型
a.y : ベクトルaの$${y}$$成分 float型
とするだけで、ベクトルの成分の値を取得できます。

2つのベクトルの大きさ、内積、なす角を計算するプログラム

ベクトルの成分を取得できるようになりましたので、問題(1)を例にしてベクトルの大きさや内積についてベクトルの成分を用いて計算をして、最後になす角を算出するプログラムを作成します。
今回は、ベクトルを平面上に描くことも行いたいので、記事『高校数学をプログラミングで解く(数学B編)「1-0 ベクトルを描く」』で作成したスケッチ「drawVector」を再利用して作成していきます。
スケッチ「drawVector」をフォルダごとコピーして、スケッチの名前(フォルダ名)を「calcInnerProduct」と変更し、またスケッチ「calcInnerProduct」内の「drawVector.pde」ファイルの名前を「calcInnerProduct.pde」に変更します。そして、pdeファイル「calcInnerProduct.pde」をダブルクリックしてスケッチ「calcInnerProduct」の開発環境ウィンドウを立ち上げます。開発環境ウィンドウのタブ欄で「calcInnerProduct」タブを選択し、そのテキストエリアのソースコードを以下で書き換えます。

// 2つのベクトルの大きさ、内積、なす角を算出する、2つのベクトルを描く
void setup(){
  size(500,500,P2D);
  noLoop();
  float x_range = 10.0; // x軸の表示範囲 -x_rangeからx_rangeまで
  float y_range = 10.0; // y軸の表示範囲 -y_rangeからy_rangeまで 
  setAxes(x_range, y_range); // 座標軸の準備
  
  // 2つのベクトルを生成
  PVector a = new PVector(sqrt(3.0), 3.0);
  PVector b = new PVector(3.0, sqrt(3.0));
  
  // 2つのベクトルの大きさ
  float magnitude_a = sqrt(a.x * a.x + a.y * a.y);
  float magnitude_b = sqrt(b.x * b.x + b.y * b.y);
  
  // 2つのベクトルの内積を計算する
  float innerproduct = a.x * b.x + a.y * b.y;
 
  // なす角を求める
  float cos_theta = innerproduct / magnitude_a / magnitude_b;
  float theta = 180.0 / PI * acos( cos_theta );
  
  println( "ベクトルaの大きさ:", magnitude_a);
  println( "ベクトルbの大きさ:", magnitude_b);
  println( "ベクトルの内積:", innerproduct);
  println( "ベクトルのなす角:", theta);
  
  // 以下にグラフを描いていく
  noFill(); // グラフの中身を塗りつぶさない
  stroke(0,0,0); // グラフの線の色を黒色に設定

  // キャンバスにベクトルa, bを描画
  arrow(0.0, 0.0, a.x, a.y, x_range);
  arrow(0.0, 0.0, b.x, b.y, x_range);

}

ソースコード1 2つのベクトルの大きさ、内積、なす角を求めるプログラム

このスケッチ「calcInnerProduct」を実行すると、図1のように、コンソールに各ベクトルの大きさ、2つのベクトルの内積、2つのベクトルのなす角が順に、

ベクトルaの大きさ: 3.4641016
ベクトルbの大きさ: 3.4641016
ベクトルの内積: 10.392304
ベクトルのなす角: 29.999992

と表示されます。

図1 スケッチ「calcInnerProduct」の実行結果

この結果は、
$${\vec{a}}$$の大きさ:$${2 \sqrt{3} = 3.4641016 \cdots}$$
$${\vec{b}}$$の大きさ:$${2 \sqrt{3} = 3.4641016 \cdots}$$
$${\vec{a} \cdot \vec{b}}$$:$${6 \sqrt{3} = 10.392304 \cdots}$$
なす角:$${30^{\circ}}$$
と丸め誤差の範囲で一致していることがわかります。
また、図2のように、実行ウィンドウのキャンバスにそれぞれのベクトルが矢印で描かれます。

図2 問題(1)の2つのベクトルを描画

プログラムの解説「なす角を求める」

ここでは、ソースコード1で2つのベクトルのなす角$${\theta}$$を求めている部分

  float cos_theta = innerproduct / magnitude_a / magnitude_b;
  float theta = 180.0 / PI * acos( cos_theta );

について、解説しておきます。

$${\cos \theta}$$の計算
2つのベクトル$${\vec{a}, \vec{b}}$$のなす角$${\theta}$$については、上記で説明したように、

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

という式を満たします。つまり、2つのベクトルの大きさ$${| \vec{a} |}$$、$${| \vec{b} |}$$と、2つのベクトルの内積$${\vec{a} \cdot \vec{b}}$$は事前に計算していますので、それらの値を用いて$${\cos \theta}$$を計算することができます。ソースコード1では、$${\cos \theta}$$の値をfloat型変数 cos_theta に代入しています。

$${\theta}$$の計算
$${\cos \theta}$$の値が計算できたので、これから$${\theta}$$を算出するだけです。ここで扱っている問題は、$${\cos \theta}$$の値から$${\theta}$$の値を計算しやすいものとなっています。例えば、問題(1)では、

$$
\cos \theta = \frac{ \sqrt{3} }{2}
$$

となります。この場合は$${30^{\circ}}$$、$${60^{\circ}}$$、$${90^{\circ}}$$の直角三角形を考えると、

$$
\cos 30^{\circ} = \frac{ \sqrt{3} }{2}
$$

となるので、

$$
\theta = 30^{\circ}
$$

となります。
ただ、このような比較する方法を用いて算出する方法をアルゴリズム化することは難しいですし、また、一般的に2つのベクトルのなす角がわかりやすい値であることはめったにありません。
そこで、ソースコード1では、$${\cos}$$関数の逆関数$${\arccos}$$を利用しています。逆関数$${\arccos}$$はその名の通り、$${y = \cos \theta}$$とすると、$${\theta = \arccos y}$$となります。Processingではこの逆関数$${\arccos}$$を「acos」と表します。

float theta = acos( cos_theta );

引数と返り値は以下の通りです。
cos_theta:$${\cos \theta}$$の値 float型($${-1 \leq}$$ cos_theta $${\leq 1}$$)
返り値:$${\cos \theta =}$$cos_thetaとなる$${\theta}$$の値(ラジアン)float型($${0 \leq \theta \leq \pi }$$)

弧度法から度数法への変換
acos 関数を利用すると、なす角$${\theta}$$を求めることができますが、その値の単位は弧度法(ラジアン)で表されています。数学Bの段階では、度数法(°)で表されている方がわかりやすいので、最後に弧度法から度数法に変換しておきます。と言ってもそんなに難しいことではなく、$${\pi}$$ラジアンと180°が同じであることを利用すると、

$$
\theta \mathrm{(ラジアン)}  \to \frac{ 180 }{ \pi } \theta (^{\circ})
$$

と$${180/\pi}$$倍することで、簡単に変更できます。

その他の問題

残りの問題(2)-(4)についてもソースコード1の

  // 2つのベクトルを生成
  PVector a = new PVector(sqrt(3.0), 3.0);
  PVector b = new PVector(3.0, sqrt(3.0));

の部分を書き換えて、実行するだけで解を求めることができます。以下に、出力結果を示しておきます。

問題(2)
ベクトルaの大きさ: 6.708204
ベクトルbの大きさ: 6.3245554
ベクトルの内積: -30.0
ベクトルのなす角: 135.0

図3 問題(2)のベクトル

問題(3)
ベクトルaの大きさ: 2.236068
ベクトルbの大きさ: 6.708204
ベクトルの内積: 0.0
ベクトルのなす角: 90.0

図4 問題(3)のベクトル

問題(4)
ベクトルaの大きさ: 1.4142135
ベクトルbの大きさ: 3.8637033
ベクトルの内積: 2.732051
ベクトルのなす角: 59.999992

図5 問題(4)のベクトル

PVectorクラスの内積を計算する関数を用いるプログラム

前節『ベクトルの成分を用いて内積を計算するプログラム』では、PVectorクラスの変数 a に対して、a.xやa.yとすることでベクトルの$${x}$$成分や$${y}$$成分を取り出すことができることを利用して、2つのベクトルの大きさや内積、そしてなす角を計算しましたが、実はPVectorクラスには、ベクトルの大きさや2つのベクトルの内積、2つのベクトルのなす角を求めるための関数が用意されています。ここでは、それらの関数を紹介したあと、それらの関数を用いて、ソースコード1を書き換えます。

ベクトルの大きさを求める関数「mag」

PVectorクラスの変数 a で表したベクトルに対してその大きさを求めるときは、mag関数を用います。

PVector a = new PVector(a1, a2);
a.mag();

mag関数は引数を取りませんが、返り値はベクトル a の大きさ(float型)となります。

2つのベクトルの内積を求める関数「dot」

PVectorクラスの変数 a, b で表した2つのベクトルに対してそれらの内積を求めるときは、dot関数を用います(内積のことをドット(dot)積とも呼ぶようです)。

PVector a = new PVector(a1, a2);
PVector b = new PVector(b1, b2);
b.dot(a);

dot関数の引数は内積を算出したいベクトル(PVectorクラス)を取り、返り値は2つのベクトルの内積(float型)となります。

2つのベクトルのなす角を求める関数「angleBetween」

PVectorクラスの変数 a, b で表した2つのベクトルに対してそれらのなす角を求めるときは、angleBetween関数を用います。

PVector a = new PVector(a1, a2);
PVector b = new PVector(b1, b2);
PVector.angleBetween(a, b);

angleBetween関数の引数は2つのベクトル(PVectorクラス)を取り、返り値は2つのベクトルのなす角(ラジアン、float型)となります。なお、mag関数やdot関数と違い「PVector.angleBetween」という形で利用することに注意してください。

2つのベクトルの大きさ、内積、なす角を計算するプログラムの書き換え

それでは、上記のPVectorクラスの関数を利用して、2つのベクトルの大きさ、内積、なす角を計算するプログラム(ソースコード1)を書き換えます。

// 2つのベクトルの大きさ、内積、なす角を算出する、2つのベクトルを描く
void setup(){
  size(500,500,P2D);
  noLoop();
  float x_range = 10.0; // x軸の表示範囲 -x_rangeからx_rangeまで
  float y_range = 10.0; // y軸の表示範囲 -y_rangeからy_rangeまで 
  setAxes(x_range, y_range); // 座標軸の準備
  
  // 2つのベクトルを生成
  PVector a = new PVector(sqrt(3.0), 3.0);
  PVector b = new PVector(3.0, sqrt(3.0));
  
  // 2つのベクトルの大きさ
  float magnitude_a = a.mag();
  float magnitude_b = b.mag();
  
  // 2つのベクトルの内積を計算する
  float innerproduct = b.dot(a);
  
  // なす角を求める
  float theta = 180.0 / PI * PVector.angleBetween(a,b);
  
  println( "ベクトルaの大きさ:", magnitude_a);
  println( "ベクトルbの大きさ:", magnitude_b);
  println( "ベクトルの内積:", innerproduct);
  println( "ベクトルのなす角:", theta);
  
  // 以下にグラフを描いていく
  noFill(); // グラフの中身を塗りつぶさない
  stroke(0,0,0); // グラフの線の色を黒色に設定

  // キャンバスにベクトルa, bを描画
  arrow(0.0, 0.0, a.x, a.y, x_range);
  arrow(0.0, 0.0, b.x, b.y, x_range);

}

ソースコード2 ソースコード1の一部をPVectorクラスの関数で置き換え

スケッチ「calcInnerProduct」をフォルダごとコピーして、スケッチの名前(フォルダ名)を「calcInnerProduct2」と変更し、またスケッチ「calcInnerProduct2」内の「calcInnerProduct.pde」ファイルの名前を「calcInnerProduct2.pde」に変更します。そして、pdeファイル「calcInnerProduct2.pde」をダブルクリックしてスケッチ「calcInnerProduct2」の開発環境ウィンドウを立ち上げます。開発環境ウィンドウのタブ欄で「calcInnerProduct2」タブを選択し、そのテキストエリアのソースコードをソースコード2で書き換えて、実行します。

図6 スケッチ「calcInnerProduct2」の実行結果

結果として、コンソールに各ベクトルの大きさ、2つのベクトルの内積、2つのベクトルのなす角が順に、

ベクトルaの大きさ: 3.4641016
ベクトルbの大きさ: 3.4641016
ベクトルの内積: 10.392304
ベクトルのなす角: 30.000002

と表示されます。これは、ソースコード1のときと丸め誤差の範囲内で一致しているということがわかります。
また、図7のように、実行ウィンドウのキャンバスにそれぞれのベクトルが矢印で描かれます。これは、図2と同じものです。

図7 問題(1)の2つのベクトルを描画(図2と同じ)

まとめ

今回は、数学Bで学ぶ「ベクトルの内積」について、ベクトルの内積を利用する問題を、ベクトルの成分を用いて内積を計算する方法とプログラミング言語 Processing で用意された内積を計算する関数を用いる方法の2つの方法でプログラムを作成して解いてみました。
PVectorクラスに用意された関数を利用すると、ベクトルの大きさ、2つのベクトルの内積、及び2つのベクトルのなす角をベクトルの成分を用いて一から計算する式を書くよりももう少しプログラムが簡潔に書くことができます。
PVectorクラスには、Processingでベクトル演算を行う際に便利な関数が他にもいろいろと存在しています。英語のサイトですが、ProcessingのマニュアルにPVectorクラスの関数がまとまっていますので、興味のある方はぜひのぞいてみてください。

参考文献

改訂版 教科書傍用 スタンダード 数学B(数研出版、ISBN9784410209468)

この記事が気に入ったらサポートをしてみませんか?