[ 前へ | 目次 | 次へ ]
Now Loading...
ダウンロード
PC (※スマートフォンでは動きません) でダウンロードし、ZIPファイルを右クリックメニューから展開して、できたフォルダ内の「 VCSSL.bat(バッチファイル) 」をダブルクリックすると起動します。 Linux等では「 VCSSL.jar 」をコマンド実行してください。
» 詳しい使用方法や、エラーで展開できない際の対応方法などはこちら

入力された数式を積分して値とグラフを表示するツール

このVCSSLプログラムは、 画面上で数式を入力すると、それを数値的に積分し、 値とグラフを表示してくれるGUIツールです。 数値積分のアルゴリズムは、 シンプソン法台形法、および 矩形法(長方形近似) から選択できます。

[ 数値積分シリーズの連載記事 ]

使用方法

ダウンロードと展開(解凍)

まず、PC(スマホは未対応)で上の画面の「 ダウンロード 」ボタンを押してください。 するとZIP形式で圧縮されたファイルがダウンロードされます。

Windows をご使用の方は、ここでまずZIPファイルを右クリックし、「プロパティ」を選んで開かれる画面で、 下の方にあるセキュリティ項目の「許可する」にチェックを入れて「OK」で閉じてください。 これを行わないと、ZIP展開やソフト起動時に、警告メッセージが出て展開完了/起動できない場合があります。

その後、ZIPファイルを右クリックして「すべて展開」や「ここに展開」などで展開(解凍)してください。 展開が成功すると、ZIPファイルと同じ名前のフォルダができ、その中にZIPファイルの中身が入っています。

» 展開がエラーで止まってしまう場合や、ファイル名が文字化けしてしまう場合は…

プログラムの起動

Windows をご使用の場合

上記でZIPファイルを展開したフォルダ内にある、以下のバッチファイルをダブルクリック実行してください:

VCSSL__ダブルクリックでプログラム実行.bat

もしプログラムを書き変えながら使いたい場合は、代わりに「 VCSSL_Editor__プログラム編集はこちら.bat 」を実行してください。

正常に起動できると、初回のみ、Java実行環境を入手するか等を尋ねられるので、適時答えて済ませると、プログラムが起動します。 2回目以降はすぐに起動します。

» うまく起動できずにエラーになってしまう場合は…

Linux 等をご使用の場合

ZIPファイルを展開したフォルダ内へコマンドライン端末で cd して、以下の通り入力して実行してください:

java -jar VCSSL.jar
(プログラムの内容を書き変えながら使いたい場合は、代わりに VCSSL_Editor.jar を実行)

» javaコマンドが使用できない等のエラーが表示される場合は…

起動後

画面の様子

起動すると、まず2つの画面が立ち上がります。 背後の黒いウィンドウがコンソール画面、手前の左にある白いウィンドウが入力画面です。 入力画面上の「 CALC - 計算 」ボタンを押すと、さらにグラフ画面(右)が起動します。

コンソール画面(背後)と入力画面(左)およびグラフ画面(右)

数式と積分区間を入力して積分する

まず、積分したい数式(被積分関数)を、入力画面の「 f(x) = 」の入力欄に入力してください。 式の書き方のポイントは以下の通りです:

- 数式の書き方のポイント -
  • 式は半角(123などの、幅の細い文字)で書いてください。キーボード左上の「半角/全角」キーで切り替えられます。
  • 「 x 」は大文字ではなく小文字を用いてください。
  • 四則演算が普通に使用できます。ただし、かけ算は「 * 」、割り算は「 / 」の記号を用いてください。足し算や引き算は普通に「 + 」と「 - 」を用います。
  • かけ算や割り算は、足し算や引き算よりも先に計算されます(手計算と同じです)。
  • sin や cos といった数学関数が使用できます。また、円周率の値は「 PI 」と書けばOKです。 使用可能な数学関数・定数の一覧は、Mathライブラリの仕様書ページをご覧ください。
  • 累乗は「 ** 」の記号を用いてください。例えば x の2乗なら「 x**2 」です。

数式を入力したら、続いて「 b (x_max) = 」および「 a (x_min) = 」の入力欄に、それぞれ積分区間の上端および下端の値を入力してください。 必要なら、ここでも数式や sin や cos などの数学関数を使用する事ができます。また、円周率の値は「 PI 」として使用できます。

入力画面の様子

数式と積分区間を入力したら、 入力画面の「 CALC - 計算 」ボタンを押すと、その数式を積分する計算が実行され、結果の値がボタンの下に表示されます。 積分結果の値は、10桁に丸めて表示されます( 丸めていない元の値は、コンソール画面(黒い画面)の最終行の右の列の値から確認できます )。

グラフの描画

上記の通り「 CALC - 計算 」ボタンを押した時点で、 積分区間の上端 b の値を、下端 a から指定値まで動かした、積分過程のグラフも表示されます。 横軸が b の値、縦軸が積分値です。下端 a の値は、グラフ上では横軸の最小値(左端)の値として確認できます。 このグラフはつまるところ、被積分関数 f(x) を積分した関数 F(x) + C を、横軸を x として描いたグラフと同じ形をしています(ただし、a の設定値に応じて、上下に平行移動されています)。

グラフの様子

グラフの目盛りや範囲などを綺麗に揃えたい場合や、ラベルやタイトルを編集したい場合などは、グラフ画面上部のメニューバーから細かい調整を行う事ができます。

なお、後述する N の設定値によっては、グラフ用のデータを用意する処理に時間がかかるため、 大きいNの設定で使っていて、重いと感じたら「 Plot Graph - グラフを描画する 」のチェックを外してください。 そうするとグラフは描かれなくなりますが、計算は素早く完了するようになります。

ただ、本質的な積分可能性(リーマン可積分かどうか)にも関わる問題として、被積分関数が無限大に発散している箇所があったりすると積分も吹っ飛んでおかしくなったり、 また、滑らかでない関数の場合は積分値が収束しない場合もあります。 従って、初めて積分する数式は、 一度は粗い N でグラフを描いてみて、そもそも積分値が収束しそうなのかを確認する事をお勧めします。 積分ではなく元の関数のグラフも確認したい場合は、 「 y = f(x) の形の数式を2Dグラフとして描画するツール 」も併せてご利用ください。

グラフを別のグラフソフトで描きたい場合は…

ところで、グラフに描画されているデータは、背後の黒いコンソールウィンドウに、「 x    y 」の2カラム形式で書き出されています。 もし、グラフを別のグラフソフトで描きたい場合は、コンソールウィンドウ上部のメニューバーから 「 File 」 > 「 Save File - コンソールの内容をファイルに保存 」 を行って、 保存したファイルをグラフソフトで開いてください。

参考までに、このプログラムで起動されるグラフ画面は、 「 リニアングラフ2D( RINEARN Graph 2D ) 」というデータ解析用のグラフソフトで、 単体でも入手・利用できます。

- バグ修正のお詫び -
2019年01月20日公開のバージョンにおいて、入力した式の中における、整数同士の除算の結果が整数として解釈され、小数部が落ちるバグが存在しました。 2019年1月31日公開のバージョンにおいて修正いたしました。 ご迷惑をお掛けいたしました事をお詫び申し上げます。

数値積分アルゴリズムの選択と、刻み数 N の設定

さて、数値的に積分を計算する際に、精度を左右する重要なテーマが、 数値積分アルゴリズムの選択と、刻み数 N の値をどれくらいにするかです。 このプログラムでは、数値積分アルゴリズムは入力画面上の「 Algorithm = 」の選択欄で、 N の値は「 N = 」の入力欄で指定できます。 以下では、そもそもそれらのアルゴリズムや N の値が、どういう意味のものなのかについて、簡単に説明します。

このプログラムでは、シンプソン法、台形法(台形公式)、および矩形法(長方形近似)の3つをサポートしています。 各アルゴリズムについては、下記の記事でもそれぞれ個別に解説していますので、詳しく読みたい場合はそちらをご参照ください:

ここでは、簡単なイメージを駆け足で見てみましょう。 まず前提として、被積分関数 f(x) を、a から b までの区間で定積分した値は、下図のように、その区間内で f(x) と x 軸に挟まれた領域の面積に相当します。 従って、この面積を求めれば、積分値が得られるわけです。

f(x) と x 軸に挟まれた領域の面積

矩形法(長方形近似)

一番単純な数値積分アルゴリズムである矩形法(長方形近似)では、この面積を、微小な N 個の領域に区切って、その微小領域の面積を長方形で近似します。 そして、その N 個の微小領域の長方形を並べて、それらの面積を足す事で、積分区間全体での面積を求めます。イメージとしては下図の通りです。

積分区間を微小領域で区切るイメージ

上の図から、積分区間を微小領域で刻む数 N を増やしたほうが、微小領域の刻みが細かくなって、 上のギザギザではみ出したり欠けている箇所が減り、 f(x) と x 軸との間の面積をよく近似できるようになりそうな事が想像できると思います。 これは実際その通りで、矩形法では、N を1桁増やすと、積分値の信頼できる桁数がだいたい 1 桁程度増える事が見込めます。

台形法(台形公式)

続いて台形法です。これは上で説明した矩形法をちょっとだけ改良したもので、長方形の上の辺を、f(x) の変化に沿うように傾けます。これによって長方形は台形になりますね。 あとは、N 個の微小領域の台形を並べて、それらの面積を足す事で、積分区間全体での面積を求めます。イメージは下図です。

積分区間を台形を並べて近似するイメージ

上図を見ると、最初の矩形法の場合と比べて、ギザギザにはみ出したり欠けたりしていた部分が軽減されて、直感的にも面積の近似精度が上がっていそうに見えます。 実際その通りで、台形法では、N を1桁増やすと、積分値の信頼できる桁数がだいたい 2 桁程度増える事が見込めます。

ただ、台形法では微小領域の上の辺を直線で近似しているので、f(x) の微妙な丸みなどは真似しきれません。f(x) が直線でもない限り、 拡大すると少し面積が食い違っているはずです(下図)。これが台形法の誤差を生み出しています。

台形近似の誤差のイメージ

シンプソン法

「 それなら、上の辺を曲線で近似すれば、もっと精度が上がるのでは? 」と思いませんか? …という事で、上図の台形の上の辺を、f(x) の丸みに良い感じでフィットする二次関数で置き換えたのがシンプソン法です。下図の通りです。

長方形の上の辺を二次関数で置き換えるイメージ

シンプソン法では、この「 上の辺を二次関数でかじった短冊のようなもの 」を N 個並べる事で、面積を足し上げて積分値を求めます。 f(x) の丸みをそれなりに(2次成分まで)真似できるので、少なくとも直線で真似る台形法よりは精度が上がりそうですね。 実際その通りで、シンプソン法では N を1桁増やすと、積分値の信頼できる桁数がだいたい 4 桁程度増える事が見込めます。 台形法は2桁程度だったので、だいぶ違いますね。 ( 一方で、被積分関数 f(x) とアルゴリズムとの相性問題のようなものもあり、必ずしも高次のアルゴリズムが万能とも言い切れないのも複雑なところです。)

と、やや長い前置きになってしまいましたが、数値積分アルゴリズムと N のイメージが大まかには掴めたでしょうか。

刻み数 N の調整とアルゴリズム選択

さて、重要なのは N の値です。 N は、あくまでも基本的にですが、大きいほうが上述の近似精度が向上し、誤差(厳密な積分値と、計算結果の値のずれ)が減る事が見込めます。 ただし、あまり極端に増やし過ぎると、逆に別の誤差 が効いてきてしまったりもするので、N を1桁ずつ変えながら何度か計算してみて、 積分値が概ね(求める値の桁数まで)収束しているあたりに設定するのが無難です。具体的な適正範囲は被積分関数 f(x) の凹凸の激しさなどによって異なります。

N を1桁増やすと、結果の信頼できる桁数が何桁増えるかは、既に述べた通り、アルゴリズムによって大きく異なります。 基本的には、矩形法よりも台形法の方が、また台形法よりもシンプソン法の方が、精度を出すのに有利な場面が多いでしょう。 一方で、先に少し述べた通り、被積分関数 f(x) とアルゴリズムとの相性問題のようなものもあるので、 初めて積分する関数で、精度が要求される場合には、色々変えて様子を掴むのがおすすめです。

※ N の値がかなり大きい場合に効いてくる、演算誤差の蓄積

コンピューターでの数値計算は、本来は無限桁まで続く小数の値も、有限の桁数 (このプログラムで使用している倍精度浮動小数点数では、有効桁数で大体16桁程度) で打ち切って扱っているため、 四則演算などの基本的な計算を1回行うだけでも、その度に非常に小さな誤差が発生しています(データ型にもよります)。

なので、あまりに N を大きくして、微小領域を細かく刻みすぎると、その面積を足し上げる計算の回数が膨大になってしまって、 逆に上述のような別の誤差が蓄積する事により、ある程度以上は精度を上げられなくなってしまう場合もあります ( 矩形法で試すと分かりやすいです )。

つまり、基本的には N が大きい方が、上の方で図示して説明した近似精度は向上しますが、それにはピークがあって、 それを超えると逆に演算誤差の蓄積のせいで精度が落ちていく事もあります。

コード解説

コード全体

このプログラムのコードはVCSSLで記述されています。 VCSSLはC言語系のシンプルな文法を持っているので、C系の言語に触れられた事のある方なら、 コメントを参考にしながらコード内容を比較的簡単に追う or 改造する事ができると思います。

今回のコードは、数値積分の計算処理に、GUIの画面を組み合わせて、ツールとして仕上げたものです。 数値積分のアルゴリズムをシンプルに実装したプログラムについては、以下の回で解説していますので、そちらをご参照ください:

[ 数値積分のアルゴリズム解説記事(連載) ]

また、VCSSLでのGUI画面の構築については、「 VCSSL GUI開発ガイド 」をご参照ください。

今回のコードは、サンプルコードというよりは実際に使うためのツールなので、 細部を細かくピックアップしながらの解説は割愛し、コード全体の掲載に留めておきます。 今回のコードの全体は、以下の通りです:


coding UTF-8;

import Math;
import Text;
import GUI;
import File;
import tool.Graph2D;


/** 積分値を表示する際、丸める桁数(有効桁数)です。*/
const int RESULT_ROUNDING_DIGITS = 10;

/** コンソール上のX値を丸める桁数(有効桁数)です。 */
const int X_ROUNDING_DIGITS = 10;

/** 積分する数式 f(x) のデフォルト入力値です。 */
const string DEFAULT_EXPRESSION = "exp( 3 * sin(26 * x) )";

/** 積分区間の上端のデフォルト入力値です。 */
const string DEFAULT_X_MAX = "1.0";

/** 積分区間の下端のデフォルト入力値です。 */
const string DEFAULT_X_MIN = "0.0";

/** 数値積分での区間分割数のデフォルト入力値です。 */
const string DEFAULT_X_N = "10000";

/** シンプソン法のアルゴリズム名です。 */
const string ALGORITHM_NAME_SIMPSON = "SIMPSON - シンプソン法";

/** 台形法(台形近似)のアルゴリズム名です。 */
const string ALGORITHM_NAME_TRAPEZOIDAL = "TRAPEZOIDAL - 台形法";

/** 矩形法(長方形近似)のアルゴリズム名です。 */
const string ALGORITHM_NAME_RECTANGULAR = "RECTANGULAR - 矩形法(長方形近似)";

/** アルゴリズム名を格納する配列です。 */
const string ALGORITHM_NAMES[] = { ALGORITHM_NAME_SIMPSON, ALGORITHM_NAME_TRAPEZOIDAL, ALGORITHM_NAME_RECTANGULAR };


// 以下、グラフやGUIコンポーネントIDを格納する変数

/** グラフのIDを格納します。 **/
int graph = NULL;

/** 入力画面のウィンドウのIDを格納します。 */
int window = NULL;

/** 積分する数式を入力するテキストフィールドのIDを格納します。 */
int expressionField = NULL;

/** 積分された値を出力するテキストフィールドのIDを格納します。 */
int resultField = NULL;

/** X軸の最大値を入力するテキストフィールドのIDを格納します。 */
int xMaxField = NULL;

/** X軸の最小値を入力するテキストフィールドのIDを格納します。 */
int xMinField = NULL;

/** 数値積分での区間分割数を入力するテキストフィールドのIDを格納します。 */
int xNField = NULL;

/** アルゴリズム選択項目のIDを格納します。 */
int algorithmSelectField = NULL;

/** グラフをプロットするかどうかを選択するチェックボックスのIDを格納します。 */
int plotCheckBox = NULL;

/** CALCボタンのIDを格納します。 */
int calcButton = NULL;



/**
 * プログラムの開始時に自動で実行されます。
 */
void main(){

	// 入力画面を起動
	createInputWindow();

	// ※ ユーザーがボタンを押すとグラフが描かれる処理などは、
	// 後で定義している onButtonClick 関数を起点として実行されます。
}


/**
 * 文字列として渡された式を矩形法(長方形近似)で数値積分し、結果を返します。
 * 
 * @param fxEexpr 被積分関数の式(文字列)
 * @param xMin 積分区間の下端
 * @param xMax 積分区間の上端
 * @param n 数値積分の区間分割数
 * @param plotGraph 積分過程をグラフに描画するかどうか(trueなら描画する)
 * @return 積分結果の値
 */
float integrateRectangular(string fxExpr, float xMin, float xMax, int n, bool plotGraph) {
	
	// コンソールの内容をクリア
	clear();
	
	// 数値積分の微小区間の幅
	float dx = (xMax-xMin) / n;
	
	// 積分値
	float value = 0.0;
	
	// f(x) を微小区間ごとに二次関数近似し、面積を足していくループ(シンプソン法)
	for (int i=0; i<n; i++) {
		
		// 微小区間の左端のX値
		float xLeft = xMin + i * dx;
		
		// 積分過程をグラフに描く場合は、毎ループの先頭で x と積分値をコンソールに出力
		if (plotGraph) {
			println( round(xLeft, X_ROUNDING_DIGITS, System.HALF_UP_SIGNIF), value);
		}
		
		// 被積分関数 f(x) の式の中で参照する x 値の変数
		float x;
		
		// 微小区間左端での被積分関数 f(xLeft) の値を求める
		x = xLeft;
		float fxLeft = feval(fxExpr, 0.0); // fevalは文字列を式として評価しfloat値にして返す関数
		
		// 微小区間の長方形(矩形)の面積を、現在の積分値に足す
		value += fxLeft * dx;
	}
	
	// 右端の点は必ずグラフに含めたい上に、グラフを描かない場合も丸めていない値を確認できるように、
	// 最後に一回コンソールに値を書き出す
	println(xMax, value);
	
	// グラフの起動と描画
	if (plotGraph) {
		
		// 2Dグラフを起動してオプションを設定
		int graph = launchGraph(fxExpr); // launchGraph 関数はこのコード内の後方で定義
		
		// コンソールに出力した内容を文字列として取得し、それをグラフに渡してプロットする
		string data = load();
		setGraph2DData(graph, data);
	}
	return value;
}


/**
 * 文字列として渡された式を台形法(台形近似)で数値積分し、結果を返します。
 * 
 * @param fxEexpr 被積分関数の式(文字列)
 * @param xMin 積分区間の下端
 * @param xMax 積分区間の上端
 * @param n 数値積分の区間分割数
 * @param plotGraph 積分過程をグラフに描画するかどうか(trueなら描画する)
 * @return 積分結果の値
 */
float integrateTrapezoidal(string fxExpr, float xMin, float xMax, int n, bool plotGraph) {
	
	// コンソールの内容をクリア
	clear();
	
	// 数値積分の微小区間の幅
	float dx = (xMax-xMin) / n;
	
	// 積分値
	float value = 0.0;
	
	// f(x) を微小区間ごとに二次関数近似し、面積を足していくループ(シンプソン法)
	for (int i=0; i<n; i++) {
		
		// 微小区間の左端のX値
		float xLeft = xMin + i * dx;
		
		// 積分過程をグラフに描く場合は、毎ループの先頭で x と積分値をコンソールに出力
		if (plotGraph) {
			println( round(xLeft, X_ROUNDING_DIGITS, System.HALF_UP_SIGNIF), value);
		}
		
		// 被積分関数 f(x) の式の中で参照する x 値の変数
		float x;
		
		// 微小区間左端での被積分関数 f(xLeft) の値を求める
		x = xLeft;
		float fxLeft = feval(fxExpr, 0.0); // fevalは文字列を式として評価しfloat値にして返す関数
		
		// 微小区間右端での被積分関数 f(xLeft + dx) の値を求める
		x = xLeft + dx;
		float fxRight = feval(fxExpr, 0.0);
		
		// 微小区間の台形の面積を、現在の積分値に足す
		value += (fxLeft + fxRight) * dx / 2.0;
	}
	
	// 右端の点は必ずグラフに含めたい上に、グラフを描かない場合も丸めていない値を確認できるように、
	// 最後に一回コンソールに値を書き出す
	println(xMax, value);
	
	// グラフの起動と描画
	if (plotGraph) {
		
		// 2Dグラフを起動してオプションを設定
		int graph = launchGraph(fxExpr); // launchGraph 関数はこのコード内の後方で定義
		
		// コンソールに出力した内容を文字列として取得し、それをグラフに渡してプロットする
		string data = load();
		setGraph2DData(graph, data);
	}
	return value;
}


/**
 * 文字列として渡された式をシンプソン法で数値積分し、結果を返します。
 * 
 * @param fxEexpr 被積分関数の式(文字列)
 * @param xMin 積分区間の下端
 * @param xMax 積分区間の上端
 * @param n 数値積分の区間分割数
 * @param plotGraph 積分過程をグラフに描画するかどうか(trueなら描画する)
 * @return 積分結果の値
 */
float integrateSimpson(string fxExpr, float xMin, float xMax, int n, bool plotGraph) {
	
	// コンソールの内容をクリア
	clear();
	
	// 数値積分の微小区間の幅
	float dx = (xMax-xMin) / n;
	
	// 積分値
	float value = 0.0;
	
	// f(x) を微小区間ごとに二次関数近似し、面積を足していくループ(シンプソン法)
	for (int i=0; i<n; i++) {
		
		// 微小区間の左端のX値
		float xLeft = xMin + i * dx;
		
		// 積分過程をグラフに描く場合は、毎ループの先頭で x と積分値をコンソールに出力
		if (plotGraph) {
			println( round(xLeft, X_ROUNDING_DIGITS, System.HALF_UP_SIGNIF), value);
		}
		
		// 被積分関数 f(x) の式の中で参照する x 値の変数
		float x;
		
		// 微小区間左端での被積分関数 f(xLeft) の値を求める
		x = xLeft;
		float fxLeft = feval(fxExpr, 0.0); // fevalは文字列を式として評価しfloat値にして返す関数
		
		// 微小区間右端での被積分関数 f(xLeft + dx) の値を求める
		x = xLeft + dx;
		float fxRight = feval(fxExpr, 0.0);
		
		// 微小区間中央での被積分関数 f(xLeft + dx/2) の値を求める
		x = xLeft + dx / 2.0;
		float fxCenter = feval(fxExpr, 0.0);
		
		// 上記3地点でのf(x)の値に重みをつけて微小区間面積を求め、足す
		value += ( fxLeft + fxRight + 4.0 * fxCenter ) * dx / 6.0;
	}
	
	// 右端の点は必ずグラフに含めたい上に、グラフを描かない場合も丸めていない値を確認できるように、
	// 最後に一回コンソールに値を書き出す
	println(xMax, value);
	
	// グラフの起動と描画
	if (plotGraph) {
		
		// 2Dグラフを起動してオプションを設定
		int graph = launchGraph(fxExpr); // launchGraph 関数はこのコード内の後方で定義
		
		// コンソールに出力した内容を文字列として取得し、それをグラフに渡してプロットする
		string data = load();
		setGraph2DData(graph, data);
	}
	return value;
}


/**
 * 2Dグラフを起動し、オプションやラベルなどを設定して、グラフのIDを返します。
 * 
 * @param fxExpr 被積分関数 f(x) の式(ラベルやタイトルの内容に使用)
 * @return 起動したグラフのID
 */
int launchGraph(string fxExpr) {
	string graphTitle = "∫ " + fxExpr + " dx";
	int graph = newGraph2D(480, 30, 820, 700, graphTitle);
	setGraph2DOption(graph, "WITH_LINES", true);
	setGraph2DOption(graph, "WITH_POINTS", false);
	setGraph2DLabel(graph, "b", graphTitle + "   from  a  to  b");
	return graph;
}


/**
 * 数式などを入力する、入力画面のGUIを構築して起動します。
 */
void createInputWindow(){

	int leftWidth = 140;
	int rightX = leftWidth + 10;
	int rightWidth = 260;
	int fullWidth = 400;
	int fontSize = 20;
	
	window = newWindow( 20, 30, 450, 450, "Input Window" );

	int expressionLabel = newTextLabel( 10, 10, leftWidth, 25, "f(x) =" );
	setComponentFontSize(expressionLabel, fontSize);
	mountComponent( expressionLabel, window );

	expressionField = newTextField( rightX, 10, rightWidth, 25, DEFAULT_EXPRESSION );
	setComponentFontSize(expressionField, fontSize);
	mountComponent( expressionField, window );

	int xMaxLabel = newTextLabel( 10, 60, leftWidth, 25, "b (x_max) =" );
	setComponentFontSize(xMaxLabel, fontSize);
	mountComponent( xMaxLabel, window );

	xMaxField = newTextField( rightX, 60, rightWidth, 25, DEFAULT_X_MAX );
	setComponentFontSize(xMaxField, fontSize);
	mountComponent( xMaxField, window );

	int xMinLabel = newTextLabel( 10, 90, leftWidth, 25, "a (x_min) =" );
	setComponentFontSize(xMinLabel, fontSize);
	mountComponent( xMinLabel, window );

	xMinField = newTextField( rightX, 90, rightWidth, 25, DEFAULT_X_MIN );
	setComponentFontSize(xMinField, fontSize);
	mountComponent( xMinField, window );


	int xNLabel = newTextLabel( 10, 120, leftWidth, 25, "N =" );
	setComponentFontSize(xNLabel, fontSize);
	mountComponent( xNLabel, window );

	xNField = newTextField( rightX, 120, rightWidth, 25, DEFAULT_X_N );
	setComponentFontSize(xNField, fontSize);
	mountComponent( xNField, window );


	int algorithmLabel = newTextLabel( 10, 150, leftWidth, 25, "Algorithm =" );
	setComponentFontSize(algorithmLabel, fontSize);
	mountComponent( algorithmLabel, window );

	algorithmSelectField = newSelectField( rightX, 150, rightWidth, 25, ALGORITHM_NAMES );
	mountComponent( algorithmSelectField, window );


	plotCheckBox = newCheckBox( 10, 250, fullWidth, 25, "Plot Graph - グラフを描画", true);
	setComponentFontSize(plotCheckBox, fontSize);
	mountComponent( plotCheckBox, window );


	calcButton = newButton( 10, 280, fullWidth, 50, "CALC - 計算" );
	setComponentFontSize(calcButton, fontSize);
	mountComponent( calcButton, window );

	int resultLabel = newTextLabel( 10, 350, leftWidth, 25, "[∫f(x)dx] =" );
	setComponentFontSize(resultLabel, fontSize);
	mountComponent( resultLabel, window );

	resultField = newTextField( rightX, 350, rightWidth, 25, "" );
	setComponentFontSize(resultField, fontSize);
	mountComponent( resultField, window );
}


/**
 * 入力画面上の入力項目において、式として評価するのに適さない文字を、適切な文字で置き換えます。
 */
void correctCharactersOnInputWindow() {
	
	setComponentText(
		expressionField, 
		correctCharactersInExpression(
			getComponentText(expressionField)
		)
	);
	
	setComponentText(
		xMaxField, 
		correctCharactersInExpression(
			getComponentText(xMaxField)
		)
	);
	
	setComponentText(
		xMinField, 
		correctCharactersInExpression(
			getComponentText(xMinField)
		)
	);
	
	setComponentText(
		xNField, 
		correctCharactersInExpression(
			getComponentText(xNField)
		)
	);
}


/**
 * 文字列内で、式として評価するのに適さない文字を、適切な文字で置き換えます。
 * 
 * @param 式の内容を格納する文字列
 * @return 適切に文字を置き換えた文字列
 */
string correctCharactersInExpression(string expression) {
	string result = expression;
	
	result = replaceText(result, "×", "*", Text.ALL);
	result = replaceText(result, "÷", "/", Text.ALL);
	result = replaceText(result, "^", "**", Text.ALL);
	result = replaceText(result, "^",  "**", Text.ALL);
	
	result = replaceText(result, "+", "+", Text.ALL);
	result = replaceText(result, "*", "*", Text.ALL);
	result = replaceText(result, "/", "/", Text.ALL);
	result = replaceText(result, "ー", "-", Text.ALL);
	result = replaceText(result, "−", "-", Text.ALL);
	result = replaceText(result, "―", "-", Text.ALL);
	result = replaceText(result, "‐", "-", Text.ALL);
	result = replaceText(result, "ー", "-", Text.ALL);
	result = replaceText(result, "%", "%", Text.ALL);
	result = replaceText(result, "(", "(", Text.ALL);
	result = replaceText(result, ")", ")", Text.ALL);
	
	result = replaceText(result, "A", "A", Text.ALL);
	result = replaceText(result, "B", "B", Text.ALL);
	result = replaceText(result, "C", "C", Text.ALL);
	result = replaceText(result, "D", "D", Text.ALL);
	result = replaceText(result, "E", "E", Text.ALL);
	result = replaceText(result, "F", "F", Text.ALL);
	result = replaceText(result, "G", "G", Text.ALL);
	result = replaceText(result, "H", "H", Text.ALL);
	result = replaceText(result, "I", "I", Text.ALL);
	result = replaceText(result, "J", "J", Text.ALL);
	result = replaceText(result, "K", "K", Text.ALL);
	result = replaceText(result, "L", "L", Text.ALL);
	result = replaceText(result, "M", "M", Text.ALL);
	result = replaceText(result, "N", "N", Text.ALL);
	result = replaceText(result, "O", "O", Text.ALL);
	result = replaceText(result, "P", "P", Text.ALL);
	result = replaceText(result, "Q", "Q", Text.ALL);
	result = replaceText(result, "R", "R", Text.ALL);
	result = replaceText(result, "S", "S", Text.ALL);
	result = replaceText(result, "T", "T", Text.ALL);
	result = replaceText(result, "U", "U", Text.ALL);
	result = replaceText(result, "V", "V", Text.ALL);
	result = replaceText(result, "W", "W", Text.ALL);
	result = replaceText(result, "X", "X", Text.ALL);
	result = replaceText(result, "Y", "Y", Text.ALL);
	result = replaceText(result, "Z", "Z", Text.ALL);
	
	result = replaceText(result, "a", "a", Text.ALL);
	result = replaceText(result, "b", "b", Text.ALL);
	result = replaceText(result, "c", "c", Text.ALL);
	result = replaceText(result, "d", "d", Text.ALL);
	result = replaceText(result, "e", "e", Text.ALL);
	result = replaceText(result, "f", "f", Text.ALL);
	result = replaceText(result, "g", "g", Text.ALL);
	result = replaceText(result, "h", "h", Text.ALL);
	result = replaceText(result, "i", "i", Text.ALL);
	result = replaceText(result, "j", "j", Text.ALL);
	result = replaceText(result, "k", "k", Text.ALL);
	result = replaceText(result, "l", "l", Text.ALL);
	result = replaceText(result, "m", "m", Text.ALL);
	result = replaceText(result, "n", "n", Text.ALL);
	result = replaceText(result, "o", "o", Text.ALL);
	result = replaceText(result, "p", "p", Text.ALL);
	result = replaceText(result, "q", "q", Text.ALL);
	result = replaceText(result, "r", "r", Text.ALL);
	result = replaceText(result, "s", "s", Text.ALL);
	result = replaceText(result, "t", "t", Text.ALL);
	result = replaceText(result, "u", "u", Text.ALL);
	result = replaceText(result, "v", "v", Text.ALL);
	result = replaceText(result, "w", "w", Text.ALL);
	result = replaceText(result, "x", "x", Text.ALL);
	result = replaceText(result, "y", "y", Text.ALL);
	result = replaceText(result, "z", "z", Text.ALL);
	
	result = replaceText(result, "1", "1", Text.ALL);
	result = replaceText(result, "2", "2", Text.ALL);
	result = replaceText(result, "3", "3", Text.ALL);
	result = replaceText(result, "4", "4", Text.ALL);
	result = replaceText(result, "5", "5", Text.ALL);
	result = replaceText(result, "6", "6", Text.ALL);
	result = replaceText(result, "7", "7", Text.ALL);
	result = replaceText(result, "8", "8", Text.ALL);
	result = replaceText(result, "9", "9", Text.ALL);
	result = replaceText(result, "0", "0", Text.ALL);
	result = replaceText(result, ".", ".", Text.ALL);
	
	return result;
}


/**
 * ボタンが押された際にコールされます(イベントハンドラ)
 * 
 * @param id 押されたボタンのID
 */
void onButtonClick( int id ){

	// 「CALC」ボタンが押された場合
	if( id == calcButton ){
		
		// 画面上に入力されている文字列内で、式として評価するのに適さない文字を、適切な文字で置き換え
		correctCharactersOnInputWindow();
		
		// 以下、画面から入力内容を取得
		
		string fxExpr = getComponentText( expressionField );
		
		if( !evaluable( getComponentText( xMaxField ), 0.0 ) ){
			alert("x_maxの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( xMinField ), 0.0 ) ){
			alert("x_minの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( xNField ), 0.0 ) ){
			alert("Nの式に誤りがあります。");
			return;
		}
		
		float xMax = feval( getComponentText( xMaxField ), 0.0);
		float xMin = feval( getComponentText( xMinField ), 0.0);
		int n = ieval( getComponentText( xNField ), 0.0);
		bool plotGraph = getComponentBool( plotCheckBox );
		
		float x;
		if( !evaluable(fxExpr, 0.0) ){
			alert("f(x)の式に誤りがあります。");
			return;
		}
		
		// 選択されている数値積分アルゴリズム名を取得
		string algorithmName = getComponentText(algorithmSelectField);
		
		// アルゴリズムに応じて、数値積分を行う関数を実行( integrate...関数はこのコード内の前半で定義 )
		float value = 0.0;
		if (algorithmName == ALGORITHM_NAME_SIMPSON) {
			value = integrateSimpson(fxExpr, xMin, xMax, n, plotGraph);
			
		} else if (algorithmName == ALGORITHM_NAME_TRAPEZOIDAL) {
			value = integrateTrapezoidal(fxExpr, xMin, xMax, n, plotGraph);
			
		} else if (algorithmName == ALGORITHM_NAME_RECTANGULAR) {
			value = integrateRectangular(fxExpr, xMin, xMax, n, plotGraph);
		}
		
		// 積分結果を丸める
		if (!nan(value) && !inf(value)) {
			value = round(value, RESULT_ROUNDING_DIGITS, HALF_UP_SIGNIF);
		}
		
		// 積分結果をウィンドウ上のテキストフィールドにセット
		setComponentText( resultField, (string)value );
		
		return;
	}
}


/**
 * ウィンドウが閉じられた際にコールされます(イベントハンドラ)
 * 
 * @param id 閉じられたウィンドウのID
 */
void onWindowClose( int id ){
	if( id == window ){
		// プログラムの実行を終了する
		exit();
		return;
	}
}


/**
 * グラフが閉じられた際にコールされます(イベントハンドラ)
 * 
 * @param id 閉じられたグラフのID
 */
void onGraph2DClose( int id ){
	if( id == graph ){
		// 閉じられたグラフのリソースを破棄する
		deleteGraph2D(id);
		return;
	}
}
download/IntegralInput.vcssl

ライセンス

このVCSSL/Vnanoコード( 拡張子が「.vcssl」や「.vnano」のファイル )は実質的な著作権フリー(パブリックドメイン) である CC0 の状態で公開しています。 記事中にC言語/C++/Java言語などでのサンプルコードが掲載されいてる場合は、それらについても同様です。 そのままでのご利用はもちろん、改造や流用などもご自由に行ってください。

※ ただし、このコードの配布フォルダ内には、ダウンロード後すぐに実行できるように、 VCSSLの実行環境も同梱されており、そのライセンス文書は「 License 」フォルダ内に同梱されています (要約すると、商用・非商用問わず自由に使用できますが、使用の結果に対して開発元は一切の責任を負いません、といった具合の内容です)。 配布フォルダ内の各構成物の一覧やライセンスについては「 ReadMe_使用方法_必ずお読みください.txt 」をご参照ください。

※ Vnano の実行環境については、別途スクリプトエンジンのソースコードも一般公開しており、 何らかのソフトウェア内に組み込んでご利用いただく事も可能です。詳細はこちらをご参照ください。

この記事中の商標などについて

  • OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録商標である場合があります。
  • Windows は、米国 Microsoft Corporation の米国およびその他の国における登録商標です。この記事は独立著作物であり、Microsoft Corporation と関連のある、もしくはスポンサーを受けるものではありません。
  • Linux は、Linus Torvalds 氏の米国およびその他の国における商標または登録商標です。
  • その他、文中に使用されている商標は、その商標を保持する各社の各国における商標または登録商標です。

[ 前へ | 目次 | 次へ ]
角度の「度」とラジアンとを相互変換し、図示もするツール

45度などの「度」の値と、ラジアンの値とを相互に変換できるツールです。対応する角度の図示もできます。
FizzBuzz の答えを表示するプログラム

プログラミングの練習問題としても有名な、FizzBuzz 問題の答えを表示するプログラムの例です。
Vnano版 | 積分値のグラフ描画用データを出力するプログラム

数値的に積分を行い、結果の関数をグラフに描くためのデータを出力するコードです。
Vnano版 | 積分値を求めるプログラム (数値積分)

矩形法/台形法/シンプソン法を用いて、積分の値を数値的に求めるコードです。
入力された数式を積分して値とグラフを表示するツール

画面上で数式を入力すると、それを数値的に積分し、値とグラフを表示してくれるGUIツールです。
シンプソン法による数値積分

積分の値を数値的に求めます。台形法よりも高精度な方法として、被積分関数を微小区間内で二次関数近似して求めた面積を足しあげる、シンプソン法を使用します。
台形法(台形近似)による数値積分

積分の値を数値的に求めます。長方形近似よりも高精度な方法として、台形で近似した微小領域を足しあげる方法を使用します。
矩形法(長方形近似)による数値積分

積分の値を数値的に求めます。長方形の短冊(矩形)で近似した微小領域を足しあげる、最も単純な方法を使用します。
小数(浮動小数点数)から分数へ近似的に変換するツール

小数(浮動小数点数)を、適当な誤差の範囲内で、近い分数に変換してくれるツールプログラムです。
円周率1万桁の計算(ガウス=ルジャンドル法)

ガウス=ルジャンドル法により、円周率を1万桁まで計算するプログラムです。
試し割り法による素数判定ツール

試し割り法を用いて、素数判定を行ってくれる簡易ツールです。
この階層の目次
RINEARN からのお知らせ
※ VCSSL は RINEARN が開発しています。

各ソフトやVCSSLの英語版ドキュメント整備がほぼ完了
2025-06-30 - RINEARNでは2年前から、AIの補助による英語版ドキュメントの大幅拡充計画を進めてきました。今回、主要ドキュメント&コンテンツの英訳がほぼ完了し、一応の目標水準に達しました。詳細をお知らせします。

VCSSLの最新版をリリース: 外部プログラムとの連携機能を少し強化、他
2025-05-25 - VCSSL 3.4.52をリリースしました。外部プログラム(C言語製の実行ファイル等)との連携機能を少し強化し、文字化け対策やOS判別などを可能にしました。他にも細かい機能追加があります。詳細をお知らせします。

VCSSLの最新版をリリース、Java 24上での非互換な挙動を対処
2025-04-22 - VCSSL 3.4.50をリリースしました。Java 24環境上でのネットワークドライブ関連のファイルパス解決で、従来環境とは異なる挙動が生じていたのを解消しました。詳細をお知らせします。

リニアングラフやVCSSLの最新版をリリース、目盛りの位置や内容を自由に指定可能に!
2024-11-24 - リニアングラフ3D/2Dを更新し、自由な位置に、自由な表記内容の目盛りを描けるようになりました! 併せて、Java言語やVCSSLでの、プログラム制御用APIも拡張しています。詳細をお知らせします。

Exevalator 2.2 をリリース、TypeScript 対応によりWebブラウザ上で動作可能に
2024-10-22 - オープンソースの式計算ライブラリ「Exevalator(エグゼバレータ)」の2.1をリリースしました。新たに TypeScript に対応し、Webブラウザ上での式計算にも使えるようになりました。詳細を解説します。

アシスタントAI作成の舞台裏(その2、作成編)
2024-10-12 - アシスタントAIの作成方法解説の後編です。実際にChatGPTの「GPTs」機能を用いて、アシスタントAIを作成する手順や、独自の知識をもたせたり、精度を出すためのノウハウなどを解説しています。

アシスタントAI作成の舞台裏(その1、基礎知識編)
2024-10-07 - アシスタントAI作成方法解説の前編です。今回はまず、アシスタントAIを作る前に抑えておきたい、基礎知識を延々と解説しています。そもそもLLM型AIとはどんな存在か? RAGとは何か? 等々です。

ソフトの利用をサポートしてくれるアシスタントAIを提供開始!
2024-09-20 - RINEARN製ソフトの使い方の質問応答や、一部作業のお手伝いをしてくれる、アシスタントAIを提供開始しました。ChatGPTアカウントさえあれば、誰でも無料で使用できます。使い方を解説します。

Exevalator 2.1 をリリース、新たに Visual Basic に対応
2024-07-28 - オープンソースの式計算ライブラリ「Exevalator(エグゼバレータ)」の2.1をリリースしました。今回から、新たに Visual Basic(VB.NET)でも使用できるようになりました。詳細を解説します。

関数電卓 RINPn(りんぷん)、Esc キーで計算式の一発クリアが可能に
2024-07-20 - 関数電 RINPn の Ver.1.0.2 をリリースしました。今回から、キーボードの「 Esc 」キーを押すと、入力中の計算式を一発でクリアできるようになりました。詳細を解説します。