見出し画像

高校数学をプログラミングで解く(数学II編)「2-4 円と直線」


はじめに

今回は、数学IIで学ぶ「円と直線」について、円と直線の位置関係を判定するプログラムや円の接線を描くプログラムを作成します。

円と直線

まず、円と直線の関係について解説しておきます。

円と直線の位置関係

円の方程式$${(x-a)^2+(y-b)^2=r^2}$$と直線の方程式$${y=mx+n}$$から$${y}$$を消去して得られる$${x}$$の2次方程式の判別式を$${D}$$とします。
また、円の中心と直線の距離を$${d}$$とすると、次のことが成り立ちます。

$$
\mathrm{円と直線が}
\begin{cases}
\begin{array}{lllll}
\mathrm{異なる2点で交わる} & \Leftrightarrow & D>0 & \Leftrightarrow & d<r  \\
\mathrm{1点で接する} & \Leftrightarrow & D=0 & \Leftrightarrow & d=r  \\
\mathrm{共有点をもたない} & \Leftrightarrow & D<0 & \Leftrightarrow & d>r 
\end{array}
\end{cases}
$$

円の接線

円$${x^2+y^2=r^2}$$上の点$${(x_1,y_1)}$$における接線の方程式は

$$
x_1x+y_1y=r^2 \ \ (x_1^2+y_1^2=r^2)
$$

円と直線の関係の問題

では、円と直線の関係に関する問題をプログラミングして解いていきます。

問題1
円と直線の関係(異なる2点で交わる、接する、共有点がない)を調べ、結果をコンソールに出力せよ。また、これらの円と直線をキャンバス上に描け。
(1) $${x^2+y^2=5}$$、$${y=3x-5}$$
(2) $${x^2+y^2=2}$$、$${x+y=2}$$
(3) $${x^2+y^2+2x-4y=0}$$、$${x+2y+6=0}$$

アルゴリズム設計

今回、円と直線の関係を調べるために判別式を利用します。上記の解説で『円の方程式$${(x-a)^2+(y-b)^2=r^2}$$と直線の方程式$${y=mx+n}$$から$${y}$$を消去して得られる$${x}$$の2次方程式の判別式を$${D}$$とします。』と記述した部分を具体的に式変形してみます。
$${y}$$を消去して整理すると、$${x}$$の2次方程式

$$
(m^2+1)x^2+2 \{ m(n-b)-a \} x +a^2+(n-b)^2-r^2=0
$$

が得られます。この2次方程式の係数をそれぞれ

$$
A =m^2+1, \ B=m(n-b)-a, \ C=a^2+(n-b)^2-r^2
$$

とおくと、判別式は

$$
D=B^2-AC
$$

で計算することができます。
また、円と直線の描画について、円は記事『高校数学をプログラミングで解く(数学II編)「2-3 円の方程式」』で作成したスケッチ「drawCircles」内で利用した関数 draw_circle を再利用し、直線は記事『高校数学をプログラミングで解く(数学II編)「2-2 2直線の関係」』で作成したスケッチ「drawTwoLines」内で利用した関数 draw_equation_of_line0 を再利用します。

プログラム 

それでは、問題1(1)をベースに円と直線の関係を判定するプログラムを作成します。記事『高校数学をプログラミングで解く(数学II編)「2-3 円の方程式」』で作成したスケッチ「drawCircles」をフォルダごとコピーして、スケッチの名前(フォルダ名)を「drawCircleandLine」と変更し、またスケッチ「drawCircleandLine」内の「drawCircles.pde」ファイルの名前を「drawCircleandLine.pde」に変更します。そして、pdeファイル「drawCircleandLine.pde」をダブルクリックしてスケッチ「drawCircleandLine」の開発環境ウィンドウを立ち上げます。開発環境ウィンドウのタブ欄で「drawCircleandLine」タブを選択し、そのテキストエリアのソースコードを以下で書き換えます。

float x_range = 10.0; // x軸の表示範囲 -x_rangeからx_rangeまで
float y_range = 10.0; // y軸の表示範囲 -y_rangeからy_rangeまで 

void setup(){
  size(500,500);
  noLoop();

  setAxes(x_range, y_range); // 座標軸の準備
  
  noFill();
  stroke(0,0,0);

  // 円の中心座標と半径
  float a = 0.0;
  float b = 0.0;
  float r = sqrt(5.0);
  // 直線の傾きと切片
  float m = 3.0;
  float n = -5.0;

  // 判別式による円と直線の関係
  float D = calcDiscriminant(a,b,r,m,n);
  if(D > 0.0){
    println("異なる2点で交わる");
  } else if(D == 0.0){
    println("接する");
  } else {
    println("共有点がない");
  }

  // 円の描画
  draw_circle(a, b, r);
  // 直線の描画
  draw_equation_of_line0(m,n);
  
  
}

// 2次方程式の判別式
float calcDiscriminant(
  float a, // 円の中心のx座標
  float b, // 円の中心のy座標
  float r, // 円の半径
  float m, // 直線の傾き
  float n  // 直線の切片
){
  float A = m*m+1.0;
  float B = m*(n-b)-a;
  float C = a*a+(n-b)*(n-b)-r*r;
  
  return B*B-A*C;
}

// 中心(x,y)、半径rの円を描く関数
void draw_circle(
  float x, // 円の中心のx座標
  float y, // 円の中心のy座標
  float r  // 円の半径
){
  float X, Y; // キャンバス上の座標
  // キャンバス上の座標位置に換算
  X = width / 2.0 / x_range * x;
  Y = height / 2.0 / y_range * y;
  // 半径をキャンバス上の大きさに換算
  float R = width / 2.0 / x_range * r;
  // 円を描画
  circle(X, Y, 2.0*R);
}

// 直線の方程式 y=mx+nを描く関数
void draw_equation_of_line0(
  float m, // 傾き
  float n  // 切片
){
  // グラフの定義域
  float x_min = -x_range;
  float x_max = x_range;
  int plot_num = 200; // グラフを描くための頂点の個数  
  
  // グラフを描画
  float x, y; // 関数の座標
  float X, Y; // キャンバス上の座標 
  beginShape();
  for(int i=0; i<=plot_num; i++){
    x = x_min + (x_max - x_min) / plot_num * i; // 直線上の点のx座標
    y = m * x + n; // 直線上の点のyの値
    // キャンバス上の座標位置に換算
    X = width / 2.0 / x_range * x;
    Y = height / 2.0 / y_range * y;
    vertex(X, Y);
  }
  endShape();

}

ソースコード1 円と直線の関係を判定するプログラム

今回、アルゴリズム設計のところで説明した、円の方程式と直線の方程式から得られる$${x}$$の2次方程式の判別式について、その値を求める関数 calcDiscriminant を準備しました。そして、判別式の値から実際に円と直線の関係を判定する部分は setup 関数内で記述するようにしています。

ソースコード1を、スケッチ「drawCircleandLine」の「drawCircleandLine」タブのテキストエディタ部分に書いて実行すると、図1のように、環境ウィンドウのコンソールに「異なる2点で交わる」と出力します。

図1 問題1(1)の判定結果を出力

また、図2のように実行ウィンドウのキャンバス上に問題1(1)の円と直線が描かれます。実際に、円と直線が異なる2点で交わっていることがわかります。

図2 問題1(1)の円と直線

同様にして、問題1(2)を考えてみます。直線の方程式を

$$
y=-x+2
$$

として、ソースコード1の「円の中心座標と半径」と「直線の傾きと切片」を設定している部分について

  // 円の中心座標と半径
  float a = 0.0;
  float b = 0.0;
  float r = sqrt(2.0);
  // 直線の傾きと切片
  float m = -1.0;
  float n = 2.0;

と書き換えて、スケッチ「drawCircleandLine」を実行すると、図3のように、環境ウィンドウのコンソールに「接する」と出力します。

図3 問題1(2)の判定結果を出力

また、図4のように実行ウィンドウのキャンバス上に問題1(2)の円と直線が描かれます。実際に、円と直線が接していることがわかります。

図4 問題1(2)の円と直線

最後に、問題1(3)を考えてみます。円の方程式、直線の方程式を

$$
(x+1)^2+(y-2)^2=5, \ y=-\frac{1}{2}x-3
$$

として、ソースコード1の「円の中心座標と半径」と「直線の傾きと切片」を設定している部分について

  // 円の中心座標と半径
  float a = -1.0;
  float b = 2.0;
  float r = sqrt(5.0);
  // 直線の傾きと切片
  float m = -0.5;
  float n = -3.0;

と書き換えて、スケッチ「drawCircleandLine」を実行すると、図5のように、環境ウィンドウのコンソールに「共有点がない」と出力します。

図5 問題1(3)の判定結果を出力

また、図6のように実行ウィンドウのキャンバス上に問題1(3)の円と直線が描かれます。実際に、円と直線が共有点をもたないことがわかります。

図6 問題1(3)の円と直線


円の接線を描く

次に、円の接線を描くプログラムを作成します。

問題2
円$${x^2+y^2=25}$$の、円上の点P$${(4,3)}$$における接線を描け。

アルゴリズム設計

円$${x^2+y^2=r^2}$$の、円上の点P$${(x_1, y_1)}$$での接線の方程式は、

$$
x_1x+y_1y=r^2
$$

となることがわかっているので、この公式を利用してプログラムを書いていきます。

プログラム

では、問題2の円の接線を描くプログラムを作成します。先ほど作成したスケッチ「drawCircleandLine」を再利用します。
スケッチ「drawCircleandLine」をフォルダごとコピーして、スケッチの名前(フォルダ名)を「drawCircleandTangent」と変更し、またスケッチ「drawCircleandTangent」内の「drawCircleandLine.pde」ファイルの名前を「drawCircleandTangent.pde」に変更します。そして、pdeファイル「drawCircleandTangent.pde」をダブルクリックしてスケッチ「drawCircleandTangent」の開発環境ウィンドウを立ち上げます。開発環境ウィンドウのタブ欄で「drawCircleandTangent」タブを選択し、そのテキストエリアのソースコードを以下で書き換えます。

float x_range = 10.0; // x軸の表示範囲 -x_rangeからx_rangeまで
float y_range = 10.0; // y軸の表示範囲 -y_rangeからy_rangeまで 

void setup(){
  size(500,500);
  noLoop();

  setAxes(x_range, y_range); // 座標軸の準備
  
  noFill();
  stroke(0,0,0);

  // 円の中心座標と半径
  float a = 0.0;
  float b = 0.0;
  float r = 5.0;
  // 接点(x1,y1)
  float x1 = 4.0;
  float y1 = 3.0;

  // 円の描画
  draw_circle(a, b, r);
  // 接線の描画
  draw_tangent(r,x1,y1);

}

// 中心(x,y)、半径rの円を描く関数
void draw_circle(
  float x, // 円の中心のx座標
  float y, // 円の中心のy座標
  float r  // 円の半径
){
  float X, Y; // キャンバス上の座標
  // キャンバス上の座標位置に換算
  X = width / 2.0 / x_range * x;
  Y = height / 2.0 / y_range * y;
  // 半径をキャンバス上の大きさに換算
  float R = width / 2.0 / x_range * r;
  // 円を描画
  circle(X, Y, 2.0*R);
}

// 直線の方程式 y=mx+nを描く関数
void draw_equation_of_line0(
  float m, // 傾き
  float n  // 切片
){
  // グラフの定義域
  float x_min = -x_range;
  float x_max = x_range;
  int plot_num = 200; // グラフを描くための頂点の個数  
  
  // グラフを描画
  float x, y; // 関数の座標
  float X, Y; // キャンバス上の座標 
  beginShape();
  for(int i=0; i<=plot_num; i++){
    x = x_min + (x_max - x_min) / plot_num * i; // 直線上の点のx座標
    y = m * x + n; // 直線上の点のyの値
    // キャンバス上の座標位置に換算
    X = width / 2.0 / x_range * x;
    Y = height / 2.0 / y_range * y;
    vertex(X, Y);
  }
  endShape();

}

// 接線を描く関数
void draw_tangent(
  float r,  // 円の半径
  float x1, // 接点のx座標
  float y1  // 接点のy座標
){
  float m = -x1/y1; // 接線の傾き
  float n = r*r/y1; // 接線の切片
  draw_equation_of_line0(m,n); // 接線を描画
}

ソースコード2 円の接線を描くプログラム

今回のプログラムでは、接線を描くための関数 draw_tangent を準備しました。この draw_tangent 関数は、接線の方程式を

$$
y = -\frac{x_1}{y_1}x + \frac{r^2}{y_1}
$$

とすると、接線の傾きが$${-x_1/y_1}$$、切片が$${r^2/y_1}$$となるので、これを 関数 draw_equation_of_line0 の引数に代入して呼び出すことで接線を描くようにしています。

ソースコード2を、スケッチ「drawCircleandTangent」の「drawCircleandTangent」タブのテキストエディタ部分に書いて実行すると、図7のように、実行ウィンドウのキャンバス上に円と円の接線が描かれます。

図7 円と円の接線

まとめ

今回は、数学IIで学ぶ「円と直線」について、円と直線の位置関係を判定するプログラムや円の接線を描くプログラムを作成しました。
円と直線の位置関係を判定するプログラムでは、今回、円の方程式$${(x-a)^2+(y-b)^2=r^2}$$と直線の方程式$${y=mx+n}$$から$${y}$$を消去して得られる$${x}$$の2次方程式の判別式$${D}$$を用いて判定しました。別の方法として、円の中心と直線との距離$${d}$$と円の半径$${r}$$を比較することで判定することもできます。こちらの方法を用いたプログラムにも是非チャレンジしてみてください。
また、円の接線を描くプログラムでは、接線を描くための関数 draw_tangent を準備しました。この draw_tangent 関数は、円上の点P$${(x_1,y_1)}$$での接線が$${x_1x+y_1y=r^2}$$となることから、接線の傾きが$${-x_1/y_1}$$、切片が$${r^2/y_1}$$として、これらの値をすでに作成済みの直線の方程式を描く関数  draw_equation_of_line0 に代入して呼び出すことで接線を描くように準備しました。このように、作成した関数を再度別の関数で呼び出して再利用することで同じようなコードを何度も書く必要がなくなります。このような関数の利用方法も少しずつ覚えていってください。

参考文献

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

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