関数の処理
バイセクション法
処理対象の関数をオブジェクトに入れ、オブジェクトを受け取ることによって、そのオブジェクト内の関数を処理することにする。関数を入れるオブジェクトの親クラスを抽象クラス型として用意し、処理を行う関数は、その抽象クラス型に対する処理を行う。バイセクション法を例として、具体的に説明する。
バイセクション法では、関数が、値 c をとる x の値(根)を以下の手順で求める。
いま、関数は単調増加関数であるとする。第ステップにおいて
である区間が与えられているとする。この区間の中点
における関数値によって、次の第ステップでの区間を設定する。
であれば、、 (図1)
であれば、、
図1
上の手順を繰り返せば根の存在区間は毎回半分の長さになるので、十分小さい区間になったところでその区間の中央値を求める根の値とする。
このバイセクション法を以下のようにプログラムした。
まず、抽象クラス型を
package CFunc;
public abstract class CFunc {
public
abstract double v(double x);
}
と用意する。この抽象クラス型を処理するバイセクション法のクラス型CBisectionを次のように用意する。
package CBisection;
import CFunc.*;
//
// バイセクション法のクラス型
// クラス型 CFunc の子クラスを関数 Bisection の引数とする
// バイセクション法を適用する関数は CFunc の子クラスで実装
//
public class CBisection {
public
double c; // 関数値が c となる根を求める
public
CBisection(){}
public
CBisection( double vc ){
c
= vc;
}
//
// バイセクション法を行う関数
// 引数 CFunc の子クラスにバイセクション法と適用する関数を実装する
// 2乗根、3乗根を求める場合であるので、関数は単調増加関数であるとしている
//
public
double Bisection( CFunc func ){
double
L_B = 0.0; // バイセクション法の区間の下限値
double
U_B = 1.0; // バイセクション法の区間の上限値の初期値
while
(func.v(U_B) <= c) U_B += 1.0; // 上限値を関数値が c より大きい値になるまるで増やす
do{
double
m = (U_B + L_B) / 2.0; // 区間の中点
if
(func.v(m) < c){
L_B
= m; // 中点 m での関数値が c より小さい値であるとき
}else{
U_B
= m; // 中点 m での関数値が c より小さい値ではないとき
}
}while(Math.abs(U_B
- L_B) > 1.0e-14); // 区間の幅と求める根の精度とを比較
return
(U_B + L_B) / 2.0; // 区間の中点を求める根として返す
}
}
バイセクション法を行う関数が
public double Bisection( CFunc func )
となっているので、抽象クラス型CFuncを実装する関数に応じたバイセクション法による根を求めることができる。
サンプルプログラムでは、2乗を表す関数と3乗を表す関数が、それぞれクラス型CFunc2とCFunc3として、以下のように宣言されている。
class CFunc2 extends CFunc {
public
double v(double x){
return
x * x;
}
}
class CFunc3 extends CFunc {
public
double v(double x){
return
x * x * x;
}
}
これらのクラス型に上のバイセクション法を適用すれば、2乗根および3乗根を求めることができる。以下のアンプルプログラムを用意した。
public
static void main(String args[]){
try{
//
// キーボード入力用ストリーム cin の設定
//
BufferedReader
cin = new BufferedReader(new InputStreamReader(System.in));
CFunc2
f2 = new CFunc2(); // 2乗の関数を実装したクラス型 CFunc2 のオブジェクト f2 の設定
CFunc3
f3 = new CFunc3(); // 3乗の関数を実装したクラス型 CFunc3 のオブジェクト f3 の設定
CBisection
bi = new CBisection(); // バイセクション法のクラス型 CBisection のオブジェクト bi の設定
while(true){
System.out.print("c
= "); // 根に対応する関数値 c の入力を求める
double
c = Double.parseDouble(cin.readLine());
if
(c <= 0.0) break; // c
の値が0以下であれば while 文を抜け出す
bi.c
= c; // 根に対応する関数値 c を、バイセクション法のオブジェクト bi の c に設定
double
root2 = bi.Bisection(f2); // c
に対する2乗根を求める
double
root3 = bi.Bisection(f3); // c
に対する3乗根を求める
System.out.printf("%1.3f^1/2
= %-20.9f %1.3f^1/3 = %-20.9f\n", c, root2, c, root3);
}
}
catch(Exception
e){
System.out.println("An
exception was thrown.");
System.out.println(e.toString());
}
System.out.println("The
program ended.");
}
}
実行例を図2に示す。
図2
プログラムをまとめたものは、圧縮ファイルsrc_bisection.zipとして用意した。圧縮ファイル名src_bisection.zipをクリックしてダウンロード・解凍(展開)したパッケージファイルの利用法はこのウェブサイトで説明している。
プログラムのパッケージファイルの中は、以下のようになっている。
============ CFunc.java ===============
package CFunc;
//
// バイセクション法で用いる関数のクラス型のひな型
//
public abstract class CFunc {
//
// 関数 v(double x) がバイセクション法で使われる
// 具体的な関数の実装は、子クラスで行う
//
public
abstract double v(double x);
}
==================================
=========== CBisection.java ==============
package CBisection;
import CFunc.*;
//
// バイセクション法のクラス型
// クラス型 CFunc の子クラスを関数 Bisection の引数とする
// バイセクション法を適用する関数は CFunc の子クラスで実装
//
public class CBisection {
public
double c; // 関数値が c となる根を求める
public
CBisection(){}
public
CBisection( double vc ){
c
= vc;
}
//
// バイセクション法を行う関数
// 引数 CFunc の子クラスにバイセクション法と適用する関数を実装する
// 2乗根、3乗根を求める場合であるので、関数は単調増加関数であるとしている
//
public
double Bisection( CFunc func ){
double
L_B = 0.0; // バイセクション法の区間の下限値
double
U_B = 1.0; // バイセクション法の区間の上限値の初期値
while
(func.v(U_B) <= c) U_B += 1.0; // 上限値を関数値が c より大きい値になるまるで増やす
do{
double
m = (U_B + L_B) / 2.0; // 区間の中点
if
(func.v(m) < c){
L_B
= m; // 中点 m での関数値が c より小さい値であるとき
}else{
U_B
= m; // 中点 m での関数値が c より小さい値ではないとき
}
}while(Math.abs(U_B
- L_B) > 1.0e-14); // 区間の幅と求める根の精度とを比較
return
(U_B + L_B) / 2.0; // 区間の中点を求める根として返す
}
}
===================================
============ CkBisection.java =============
package CkBisection;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import CFunc.*;
import CBisection.*;
//
// 2乗の関数を実装
//
class CFunc2 extends CFunc {
public
double v(double x){
return
x * x;
}
}
//
// 3乗の関数を実装
//
class CFunc3 extends CFunc {
public
double v(double x){
return
x * x * x;
}
}
public class CkBisection {
public
static void main(String args[]){
try{
//
// キーボード入力用ストリーム cin の設定
//
BufferedReader
cin = new BufferedReader(new InputStreamReader(System.in));
CFunc2
f2 = new CFunc2(); // 2乗の関数を実装したクラス型 CFunc2 のオブジェクト f2 の設定
CFunc3
f3 = new CFunc3(); // 3乗の関数を実装したクラス型 CFunc3 のオブジェクト f3 の設定
CBisection
bi = new CBisection(); // バイセクション法のクラス型 CBisection のオブジェクト bi の設定
while(true){
System.out.print("c
= "); // 根に対応する関数値 c の入力を求める
double
c = Double.parseDouble(cin.readLine());
if
(c <= 0.0) break; // c
の値が0以下であれば while 文を抜け出す
bi.c
= c; // 根に対応する関数値 c を、バイセクション法のオブジェクト bi の c に設定
double
root2 = bi.Bisection(f2); // c
に対する2乗根を求める
double
root3 = bi.Bisection(f3); // c
に対する3乗根を求める
System.out.printf("%1.3f^1/2
= %-20.9f %1.3f^1/3 = %-20.9f\n", c, root2, c, root3);
}
}
catch(Exception
e){
System.out.println("An
exception was thrown.");
System.out.println(e.toString());
}
System.out.println("The
program ended.");
}
}
===========================================