見出し画像

高校数学をプログラミングで解く(数学II編)「5-2 接線」


はじめに

今回は、数学IIで学ぶ「接線」について、曲線上の点における接線と法線とを描くプログラムを作成します。

接線

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

接線

曲線$${y = f(x)}$$上の点$${\mathrm{A} (a, f(a))}$$における接線
① 接線の傾き$${m}$$ $${m=f'(a)}$$
② 接線の方程式 $${y-f(a) = f'(a)(x-a)}$$

法線

曲線上の点$${\mathrm{A}}$$を通り、$${\mathrm{A}}$$における接線に垂直な直線のことをいう。
曲線$${y = f(x)}$$上の点$${\mathrm{A} (a, f(a))}$$における法線の方程式は、

$$
f'(a) \neq 0 \mathrm{のとき} y-f(a) = -\frac{1}{f'(a)} (x-a)
$$

$$
f'(a) = 0 \mathrm{のとき} x = a
$$

図1 曲線上の点の接線と法線

接線を描く

今回は、曲線上の点における接線と法線とを描くプログラムを作成していきます。

問題
次の曲線上の点における、曲線の接線、及び法線を描け。
(1) $${ y=x^2-3x+2 }$$ $${(1,0)}$$
(2) $${ y=x^3+4 }$$ $${(-2,-4)}$$
(3) $${ y=x^3-1 }$$ $${(0,-1)}$$
(4) $${ y=x^3-3x }$$ $${(1,-2)}$$
(5) $${ y=2x^3+5x^2 }$$ $${(x=-1)}$$
(6) $${ y=5x-x^3 }$$ $${(x=2)}$$

アルゴリズム設計

今回の問題は、微分係数$${f'(a)}$$を求め、上記で定義した接線の方程式や法線の方程式をそのまま利用して描いていくことができます。今回の問題の曲線の関数$${f(x)}$$はいずれも2次関数および3次関数になっていますので、導関数$${f'(x)}$$は解析的に導出することができます。そのため、記事『高校数学をプログラミングで解く(数学II編)「5-1 微分係数、導関数」』で解説した数値微分は利用せず、解析的に導出した導関数$${f'(x)}$$を関数化して直接利用していきます。なお、本問題の導関数はそれぞれ、
(1) $${f'(x) = 2x-3}$$
(2) $${f'(x) = 3x^2}$$
(3) $${f'(x) = 3x^2}$$
(4) $${f'(x) = 3x^2-3}$$
(5) $${f'(x) = 6x^2+10x}$$
(6) $${f'(x) = 5-3x^2}$$
となります。

プログラム

それでは、曲線上の点における接線と法線とを描くプログラムを作成します。ここでは、問題(1)を例として作成します。
今回は、座標軸を描くための関数 setAxes をもっていることや、指数関数のグラフを描くための関数を準備しやすさなどを考慮して、記事『高校数学をプログラミングで解く(数学II編)「2-1 直線の方程式」』で作成したスケッチ「drawEquationOfLine1」を再利用して作成していきます。
スケッチ「drawEquationOfLine1」をフォルダごとコピーして、スケッチの名前(フォルダ名)を「drawTangentialLine」と変更し、またスケッチ「drawTangentialLine」内の「drawEquationOfLine1.pde」ファイルの名前を「drawTangentialLine.pde」に変更します。そして、pdeファイル「drawTangentialLine.pde」をダブルクリックしてスケッチ「drawTangentialLine」の開発環境ウィンドウを立ち上げます。開発環境ウィンドウのタブ欄で「drawTangentialLine」タブを選択し、そのテキストエリアのソースコードを以下で書き換えます。

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 x_min = -x_range;
  float x_max = x_range;
  draw_curve_function(x_min, x_max);

  // 接点をプロットする
  float a = 1.0; // 接点のx座標
  stroke(0,255,0);
  plot_contact_point(a);

  // 接線を描く
  stroke(255,0,0);
  draw_tangential_line(a, x_min, x_max);

  // 法線を描く
  stroke(0,0,255);
  draw_normal_line(a, x_min, x_max);

}

// 曲線を表す関数
float f(
  float x 
){
  return x*x-3.0*x+2.0;
}

// 曲線の導関数
float f_prime(
  float x
){
  return 2.0*x-3.0;
}

// 曲線を描く関数
void draw_curve_function(
  float x_min, // グラフの定義域の下限
  float x_max  // グラフの定義域の上限
){
  int plot_num = 2000; // グラフを描くための頂点の個数  
  
  // グラフを描画
  float x, y; // 関数の座標
  float X, Y; // キャンバス上の座標 
  beginShape();
  for(int i=1; i<plot_num; i++){
    x = x_min + (x_max - x_min) / plot_num * i; // 曲線上の点のx座標
    y = f(x); // 曲線上の点のyの値
    // キャンバス上の座標位置に換算
    X = width / 2.0 / x_range * x;
    Y = height / 2.0 / y_range * y;
    vertex(X, Y);
  }
  endShape();

}

// 接点をプロットする関数
void plot_contact_point(
  float a // 接点のx座標
){
  float y = f(a);
  float X, Y; // キャンバス上の座標
  // キャンバス上の座標位置に換算
  X = width / 2.0 / x_range * a;
  Y = height / 2.0 / y_range * y;
  strokeWeight(5);
  point(X, Y);
  strokeWeight(1);
}

// 接線を描く関数
void draw_tangential_line(
  float a, // 接点のx座標
  float x_min, // グラフの定義域の下限
  float x_max  // グラフの定義域の上限
){
  int plot_num = 2000; // グラフを描くための頂点の個数  
  
  // グラフを描画
  float x, y; // 関数の座標
  float X, Y; // キャンバス上の座標 
  beginShape();
  for(int i=1; i<plot_num; i++){
    x = x_min + (x_max - x_min) / plot_num * i; // 接線上の点のx座標
    y = f_prime(a) * (x-a) + f(a); // 接線上の点のyの値
    // キャンバス上の座標位置に換算
    X = width / 2.0 / x_range * x;
    Y = height / 2.0 / y_range * y;
    vertex(X, Y);
  }
  endShape();
}

// 法線を描く関数
void draw_normal_line(
  float a, // 接点のx座標
  float x_min, // グラフの定義域の下限
  float x_max  // グラフの定義域の上限
){  
  int plot_num = 2000; // グラフを描くための頂点の個数  
  
  // グラフを描画
  float x, y; // 関数の座標
  float X, Y; // キャンバス上の座標

  if( f_prime(a) == 0.0 ){
    X = width / 2.0 / x_range * a;
    line(X, -height/2.0, X, height/2.0);
  } else {
    beginShape();
    for(int i=1; i<plot_num; i++){
      x = x_min + (x_max - x_min) / plot_num * i; // 接線上の点のx座標
      y = - 1.0 / f_prime(a) * (x-a) + f(a); // 接線上の点のyの値
      // キャンバス上の座標位置に換算
      X = width / 2.0 / x_range * x;
      Y = height / 2.0 / y_range * y;
      vertex(X, Y);
    }
    endShape();
  }
}

ソースコード1 曲線上の点における接線と法線とを描くプログラム

ソースコード1で、与えられた曲線を表す関数$${f(x)}$$とその導関数$${f'(x)}$$をそれぞれ f 関数と f_prime 関数として準備しました。また、これらの関数を利用して、 曲線を描く関数 draw_curve_function 、接線を描く関数 draw_tangential_line 、法線を描く関数 draw_normal_line を準備しました。なお、描くための3つの関数は引数に描画の定義域を設定できるようにしています。
あと、法線を描く関数 draw_normal_line については、法線が$${x=a}$$の形になる場合(つまり、$${f'(a)}$$が$${0}$$になる場合)も考慮して、$${f'(a)}$$の値で場合分けして描くようにしています。

ソースコード1を、スケッチ「drawTangentialLine」の「drawTangentialLine」タブのテキストエディタ部分に書いて実行すると、図2のように、実行ウィンドウのキャンバス上に問題(1)の曲線(黒色)、接点(緑色)、接線(赤色)、法線(青色)が描かれます。

図2 問題(1)の結果

その他の問題のプログラム

他の問題(2)-(6)については、ソースコード1の

float a = 1.0; // 接点のx座標

及び、曲線の関数とその導関数の部分

// 曲線を表す関数
float f(
  float x 
){
  return x*x-3.0*x+2.0;
}

// 曲線の導関数
float f_prime(
  float x
){
  return 2.0*x-3.0;
}

を問題に合わせて書き換えて、実行するだけで接線や法線を描くことができます。そのため、問題(2)-(6)については結果のグラフのみを示しておきます。

図3 問題(2)の結果
図4 問題(3)の結果
図5 問題(4)の結果
図6 問題(5)の結果
図7 問題(6)の結果

まとめ

今回は、数学IIで学ぶ「接線」について、曲線上の点における接線と法線とを描くプログラムを作成しました。
曲線上の点での接線や法線の傾きを求めるために、曲線の方程式から曲線上の点での微分係数を求める必要があります。この微分係数は、記事『高校数学をプログラミングで解く(数学II編)「5-1 微分係数、導関数」』で解説した数値微分で求めることもできますが、今回の問題の場合は解析的に導関数を求めることができるので、このような場合はできるだけ数値微分は避け、解析的な計算を行うことをお勧めします。なぜなら、数値微分はあくまでも近似解となるからです。どうしても解析的な計算が難しいところにのみ、数値微分を利用しましょう。使いどころは、プログラミング経験を増やしていけば自ずとわかってきますので、少しずつ慣れていってください。

参考文献

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


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