見出し画像

高校数学をプログラミングで解く(数学I編)「1-4 2次関数の最大と最小」


はじめに

今回は、数学Iで学ぶ「2次関数の最大と最小」について、閉じた定義域と開いた定義域とでそれぞれ2次関数の最大値と最小値を算出するプログラムを作成します。

閉じた定義域と開いた定義域

2次関数の最大値と最小値の算出については、記事『高校数学をプログラミングで解く(数学I編)「1-1 関数とグラフ」』において一度説明していて、本記事でも利用していきますが、今回は2次関数の定義域をきちんと設定したうえで最大値や最小値について考えていきます。
特に、定義域が閉じている場合と開いている場合とで結果が変わってきますので、これら2つの場合に分けて考えます。

閉じた定義域

閉じた定義域とは、定義域の両端が定義域に含まれているときにそのように言います。例えば、定義域$${-1 \leq x \leq 1}$$は両端の$${x=-1}$$と$${x=1}$$とが定義域に含まれていますので、閉じた定義域と言えます。

開いた定義域

開いた定義域とは、定義域の両端または一方の端が定義域に含まれていないときにそのように言います。例えば、定義域$${-1 < x < 1}$$は両端の$${x=-1}$$と$${x=1}$$とが定義域に含まれていないので、開いた定義域と言えます。また、定義域$${-1 < x \leq 1}$$や定義域$${-1 \leq x < 1}$$も、一方の端が定義域に含まれていないので、開いた定義域と言います。

閉じた定義域における2次関数の最大と最小

まず、閉じた定義域における2次関数の最大値と最小値を求めることを考えます。例として、以下の問題をプログラミングで解いてみます。

問題1
次の関数の最大値と最小値を求めよ。
$${y=x^2 \ (-2 \leq x \leq 3)}$$

なお、問題1の答えは、
$${x=3}$$のとき最大値$${9}$$、$${x=0}$$のとき最小値$${0}$$
となります。

アルゴリズム設計(復習)

最大値、最小値を算出するためのアルゴリズム設計は、記事『高校数学をプログラミングで解く(数学I編)「1-1 関数とグラフ」』で説明していますが、ここで改めて掲載しておきます。ほぼ同じような方法にはなりますが、最大値と最小値の両方を書いておきます。

最大値を算出するアルゴリズム
① 最大値を格納するfloat型の変数$${y_{\mathrm{max}}}$$を準備し、できるだけ小さな値(今回は表示範囲の最小値$${-10}$$としています)で初期化しておきます。
② 定義域$${x_l \leq x \leq x_r}$$をplot_num個に分割し、小さい方から順にその$${x}$$での関数の値をもとめ、その関数の値が$${y_{\mathrm{max}}}$$の値より大きければ、$${y_{\mathrm{max}}}$$をその時の関数の値で更新します。
③ 定義域全体で②を行った結果、最後に残った$${y_{\mathrm{max}}}$$の値が最大値となります。

最小値を算出するアルゴリズム
① 最小値を格納するfloat型の変数$${y_{\mathrm{min}}}$$を準備し、できるだけ大きな値(今回は表示範囲の最大値$${10}$$としています)で初期化しておきます。
② 定義域$${x_l \leq x \leq x_r}$$をplot_num個に分割し、小さい方から順にその$${x}$$での関数の値をもとめ、その関数の値が$${y_{\mathrm{min}}}$$の値より小さければ、$${y_{\mathrm{min}}}$$をその時の関数の値で更新します。
③ 定義域全体で②を行った結果、最後に残った$${y_{\mathrm{min}}}$$の値が最小値となります。

2次関数の最大値と最小値を算出するプログラム(閉じた定義域の場合)

では、2次関数の最大値と最小値を算出するプログラムを作成します。なお、2次関数の関数は一般形($${y=ax^2+bx+c}$$)のもので用意しています。

// 2次関数の最大値と最小値を算出する
void setup(){

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

  // グラフの定義域
  float x_l = -2.0; // 定義域の左端
  float x_r = 3.0; // 定義域の右端
  int plot_num = 200; // グラフを描くための頂点の個数  
  
  // 2次関数の係数
  float a = 1.0;
  float b = 0.0;
  float c = 0.0;  
  
  // 2次関数の最大値と最小値を求める
  float x, y; // 関数の座標
  float y_max = -y_range; // 最小値 表示範囲の最小値で初期化
  float x_max = 0.0; // 関数が最小値を取るときのxの値  
  float y_min = y_range; // 最小値 表示範囲の最大値で初期化
  float x_min = 0.0; // 関数が最小値を取るときのxの値  
  for(int i=0; i<=plot_num; i++){
    x = x_l + (x_r - x_l) / plot_num * i; // 関数のx座標
    y = quadraticfunction(a,b,c,x); // 関数の値
    // 最大値を求める    
    if( y_max < y ){
      y_max = y;
      x_max = x;
    }
    // 最小値を求める
    if( y_min > y ){
      y_min = y;
      x_min = x;
    }    
  }
  println("最大値 ", x_max, y_max);
  println("最小値 ", x_min, y_min);

}

// 2次関数の一般形
float quadraticfunction(
  float a, // 2次の係数
  float b, // 1次の係数
  float c, // 定数項
  float x
){
  return a*x*x + b*x + c;
}

ソースコード1 問題1を解くプログラム

このソースコード1を、Processingの開発環境ウィンドウを開いて(スケッチ名を「max_min_QuadraticFunction」としています)、テキストエディタ部分に書いて実行すると、図1のように、コンソールに$${\mathrm{最大値} \ 3.0  \ 9.0}$$と$${\mathrm{最小値} \ 0.0 \ 0.0}$$とが表示されます。

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

グラフを描くプログラム(閉じた定義域の場合)

では、問題1の2次関数をその定義域の範囲で描いて、最大値および最小値を取る点をそれぞれプロットするプログラムを作成します。

// 2次関数のグラフと最小値を描く
void setup(){
  size(500,500);
  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次関数の係数
  float a = 1.0;
  float b = 0.0;
  float c = 0.0;  
 
  int plot_num = 200; // グラフを描くための頂点の個数  

  noFill();
  
  // 表示領域全体でグラフを描画
  stroke(128,128,128); // グレー
  float x_l = -x_range; // 定義域の左端
  float x_r = x_range; // 定義域の右端
  float x, y; // 関数の座標
  float X, Y; // キャンバス上の座標
  beginShape();
  for(int i=0; i<=plot_num; i++){
    x = x_l + (x_r - x_l) / plot_num * i; // 関数のx座標
    y = quadraticfunction(a,b,c,x); // 関数の値
    // キャンバス上の座標位置に換算
    X = width / 2.0 / x_range * x;
    Y = height / 2.0 / y_range * y;
    vertex(X, Y);
  }
  endShape();
  
  // 定義域内で最大値と最小値を算出しつつ、グラフを描画
  stroke(0,0,0); // 黒色
  x_l = -2.0; // 定義域の左端
  x_r = 3.0; // 定義域の右端
  float y_max = -y_range; // 最小値 表示範囲の最小値で初期化
  float x_max = 0.0; // 関数が最小値を取るときのxの値  
  float y_min = y_range; // 最小値 表示範囲の最大値で初期化
  float x_min = 0.0; // 関数が最小値を取るときのxの値    
  beginShape();
  for(int i=0; i<=plot_num; i++){
    x = x_l + (x_r - x_l) / plot_num * i; // 関数のx座標
    y = quadraticfunction(a,b,c,x); // 関数の値
    // 最大値を求める    
    if( y_max < y ){
      y_max = y;
      x_max = x;
    }
    // 最小値を求める
    if( y_min > y ){
      y_min = y;
      x_min = x;
    }
    // キャンバス上の座標位置に換算
    X = width / 2.0 / x_range * x;
    Y = height / 2.0 / y_range * y;
    vertex(X, Y);
  }
  endShape();
  
  // 最大値を取るグラフ上の点をキャンバス上の座標位置に換算してプロット
  stroke(255,0,0);
  strokeWeight(5);
  float X_max, Y_max;
  X_max = width / 2.0 / x_range * x_max;
  Y_max = height / 2.0 / y_range * y_max;
  point(X_max, Y_max);

  // 最小値を取るグラフ上の点をキャンバス上の座標位置に換算してプロット
  stroke(0,255,0);
  float X_min, Y_min;
  X_min = width / 2.0 / x_range * x_min;
  Y_min = height / 2.0 / y_range * y_min;
  point(X_min, Y_min);

}

// 2次関数の一般形
float quadraticfunction(
  float a, // 2次の係数
  float b, // 1次の係数
  float c, // 定数項
  float x
){
  return a*x*x + b*x + c;
}

ソースコード2 2次関数のグラフと最大値と最小値を描くプログラム(閉じた定義域)

スケッチ「drawQuadraticFunctionCloseDomain」を準備し、その「drawQuadraticFunctionCloseDomain」フォルダに「setAxes.pde」ファイルをコピーします。その上で、このソースコード2を、スケッチ「drawQuadraticFunctionCloseDomain」の
「drawQuadraticFunctionCloseDomain」タブのテキストエディタ部分に書いて実行すると、図2のように、実行ウィンドウのキャンバスに2次関数のグラフ(グレー)が描かれ、その上に定義域内のグラフ(黒色)と、その定義域内での最大値(赤色)、最小値(緑色)を取る点が描かれます。

図2 2次関数のグラフとその最大値と最小値(閉じた定義域)

練習問題1

次の関数の最大値と最小値を求めよ。
(1) $${y=-x^2 \ (-2 \leq x \leq 3)}$$
(2) $${y=x^2+2x-8 \ (1 \leq x \leq 3)}$$

これらの練習問題1の答えは、順に
(1) $${x=0}$$のとき最大値$${0}$$、$${x=3}$$のとき最小値$${-9}$$
(2) $${x=3}$$のとき最大値$${7}$$、$${x=1}$$のとき最小値$${-5}$$
となります。

ソースコード1やソースコード2の

  // 2次関数の係数
  float a = 1.0;
  float b = 0.0;
  float c = 0.0;  

  x_l = -2.0; // 定義域の左端
  x_r = 3.0; // 定義域の右端

の部分を各練習問題に合わせて書き換えるだけですので、正しい答えが出るか確かめてください。

開いた定義域における2次関数の最大と最小

次に、開いた定義域における2次関数の最大値と最小値を求めることを考えます。例として、以下の問題をプログラミングで解いてみます。

問題2
次の関数の最大値と最小値を求めよ。
$${y=x^2 \ (-2 < x < 3)}$$

問題1とは定義域が閉じているか開いているかの違いのみですが、問題2の答えは、
最大値なし、$${x=0}$$のとき最小値$${0}$$
となります。

アルゴリズム設計(開いた定義域の場合)

開いた定義域の場合でも、基本的には閉じた定義域の場合と同じアルゴリズムを利用することができます。違いは、開いた定義域の場合、算出された最大値や最小値の候補が定義域に入っているかを確認する必要があるということです。たとえば、問題2では、最大値の候補として$${x=3}$$のときの値$${9}$$があるわけですが、$${x=3}$$は定義域$${-2 < x < 3}$$に入っていないので、最大値として扱うことはできません。一方で、他に最大値の候補はないので、結局最大値はないということになります。

グラフを描くプログラム(開いた定義域の場合)

では、問題2の2次関数をその定義域の範囲で描いて、最大値および最小値を取る点をそれぞれプロットするプログラムを作成します。基本的には、ソースコード2と同じものですが、プログラムの最後に、算出した最大値と最小値が定義域に入っているかを確認して入っていればプロットし、入っていなければプロットしないという処理を入れています。

// 2次関数のグラフと最小値を描く
void setup(){
  size(500,500);
  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次関数の係数
  float a = 1.0;
  float b = 0.0;
  float c = 0.0;  
 
  int plot_num = 200; // グラフを描くための頂点の個数  

  noFill();
  
  // 表示領域全体でグラフを描画
  stroke(128,128,128); // グレー
  float x_l = -x_range; // 定義域の左端
  float x_r = x_range; // 定義域の右端
  float x, y; // 関数の座標
  float X, Y; // キャンバス上の座標
  beginShape();
  for(int i=0; i<=plot_num; i++){
    x = x_l + (x_r - x_l) / plot_num * i; // 関数のx座標
    y = quadraticfunction(a,b,c,x); // 関数の値
    // キャンバス上の座標位置に換算
    X = width / 2.0 / x_range * x;
    Y = height / 2.0 / y_range * y;
    vertex(X, Y);
  }
  endShape();
  
  // 定義域内で最大値と最小値を算出しつつ、グラフを描画
  stroke(0,0,0); // 黒色
  x_l = -2.0; // 定義域の左端
  x_r = 3.0; // 定義域の右端
  float y_max = -y_range; // 最小値 表示範囲の最小値で初期化
  float x_max = 0.0; // 関数が最小値を取るときのxの値  
  float y_min = y_range; // 最小値 表示範囲の最大値で初期化
  float x_min = 0.0; // 関数が最小値を取るときのxの値    
  beginShape();
  for(int i=0; i<=plot_num; i++){
    x = x_l + (x_r - x_l) / plot_num * i; // 関数のx座標
    y = quadraticfunction(a,b,c,x); // 関数の値
    // 最大値を求める    
    if( y_max < y ){
      y_max = y;
      x_max = x;
    }
    // 最小値を求める
    if( y_min > y ){
      y_min = y;
      x_min = x;
    }
    // キャンバス上の座標位置に換算
    X = width / 2.0 / x_range * x;
    Y = height / 2.0 / y_range * y;
    vertex(X, Y);
  }
  endShape();

  strokeWeight(5);
  // x_maxが定義域に入っているか確認
  if( x_max > x_l && x_max < x_r ){
    // 最大値を取るグラフ上の点をキャンバス上の座標位置に換算してプロット
    stroke(255,0,0);
    float X_max, Y_max;
    X_max = width / 2.0 / x_range * x_max;
    Y_max = height / 2.0 / y_range * y_max;
    point(X_max, Y_max);
  }

  // x_minが定義域に入っているか確認
  if( x_min > x_l && x_min < x_r ){
    // 最小値を取るグラフ上の点をキャンバス上の座標位置に換算してプロット
    stroke(0,255,0);
    float X_min, Y_min;
    X_min = width / 2.0 / x_range * x_min;
    Y_min = height / 2.0 / y_range * y_min;
    point(X_min, Y_min);
  }
}

// 2次関数の一般形
float quadraticfunction(
  float a, // 2次の係数
  float b, // 1次の係数
  float c, // 定数項
  float x
){
  return a*x*x + b*x + c;
}

ソースコード3 2次関数のグラフと最大値と最小値を描くプログラム(開いた定義域)

スケッチ「drawQuadraticFunctionOpenDomain」を準備し、その「drawQuadraticFunctionOpenDomain」フォルダに「setAxes.pde」ファイルをコピーします。その上で、このソースコード3を、スケッチ「drawQuadraticFunctionOpenDomain」の
「drawQuadraticFunctionOpenDomain」タブのテキストエディタ部分に書いて実行すると、図3のように、実行ウィンドウのキャンバスに2次関数のグラフ(グレー)が描かれ、その上に定義域内のグラフ(黒色)と、その定義域内での最小値(緑色)を取る点が描かれます。なお、今回は最大値がないのでその点は描かれていません。

図2 2次関数のグラフとその最大値(なし)と最小値(開いた定義域)

練習問題2

次の関数の最大値と最小値を求めよ。
(1) $${y=-x^2 \ (-2 < x < 3)}$$
(2) $${y=x^2+2x-8 \ (1 < x < 3)}$$

これらの練習問題2の答えは、順に
(1) $${x=0}$$のとき最大値$${0}$$、最小値なし
(2) 最大値、最小値ともになし
となります。

ソースコード3を書き換えて実行し、正しい結果が出るか試してみてください。

まとめ

今回は、数学Iで学ぶ「2次関数の最大と最小」について、閉じた定義域と開いた定義域とでそれぞれ2次関数の最大値と最小値を算出するプログラムを作成しました。
2次関数の最大値と最小値の算出については、記事『高校数学をプログラミングで解く(数学I編)「1-1 関数とグラフ」』ですでに説明していましたが、今回は定義域を決めた上で最大値と最小値を算出しています。
定義域は、大きく閉じた定義域と開いた定義域との2種類に分かれます。ただ、最大値と最小値を算出するアルゴリズムはほとんど変わりません。違いは、開いた定義域の場合に最大値や最小値の候補が定義域に入っているかを最後に確認する必要があるということだけです。
なお、今回の問題は開いた定義域として$${-2 < x < 3}$$のように両端が開いているものを設定しましたが、開いた定義域として$${-2 \leq x < 3}$$や$${-2 < x \leq 3}$$のような場合もあります。このような場合は、ソースコード3に追加した確認するためのプログラムの部分(if文の箇所)を設定しなおす必要があることに注意してください。

参考文献

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

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