Home >> Lecture 8


第8回 実数と利息計算(6/30)


8−1.実数計算

■実数の表記方法

実数は、連続数を表現するのに用います。連続数とは、数直線を考えたときに、どこを切っても更にその間の数があるというような性質(linearと呼ぶ)を持った数のことです。簡単に理解するには、小数点以下の数を持つ数(あるいはJava上では小数点がついている数)が実数と考えればよいでしょう。実数も2つの表現範囲を持つ型が用意されています。float型の実数は単精度浮動小数点実数(Floating point real number)と、double型の実数は倍精度浮動小数点実数(Double precision Floating point real number)と呼ばれています。
単精度実数  float  1.40129846432481707×10-45〜3.40282346638528860×10+38
倍精度実数  double  4.94065645841246544×10-324〜1.78769313486231570×10+308
実数は、内部的には国際規格のIEEE-754と呼ばれる浮動小数点表現の規格に基づいて表現されています。floatの方は、32ビットで表現されていますし、doubleの方は64ビットで表現されています。上の範囲をみればわかるように、double型の方が、より精密な数、より大きい数を表すことができます。 通常に実数の定数を書きますと、double型の定数であると解釈されますが、明示的に型の指定をすることもできます。実数定数の最後に「f」か「F」を付けると強制的にfloat型になります。同様に、実数定数の最後に「d」か「D」を付けると強制的にdouble型になります。

	型を明示した実数定数の例:	3.141459F	1.603e-45f	33.1d	  0D	   3F
	小数点の反対側を省略した例:	-.05	    	34.
		

☆実数の指数表現

実数では、指数表現が可能です。eまたはEのあとに、10のべき乗数を書きます。

	指数表現を使った実数の例:	45.6e9		3.14159E-19		5.5e+4
		
指数表現とは何でしょうか?知らない人のために、簡単に説明します。指数表現とは、以下のように、基数(ここでは10)の何乗というものを掛け合わせた実数の表現形式のことで、非常に大きい数や小さい数などを表現するときに用います。
 
通常の書き方 指数表現Javaでの指数表現
0.000005675.67×10-65.67e-6
1230000001.23×1081.23e8
たとえば、「1.23×108」という表現ですが、この「1.23」の部分を仮数(Mantissa)と呼び、「8」の部分を指数(Exponent)と呼びます。また、このように0が少なくなるように指数表現を使って桁あわせを行なうことを、実数の正規化(Normalization)と呼んでいます。IEEE-754規格で表現されているJavaの実数定数でも、限られたビットサイズで、非常に小さな数から大きな数を表すために、内部的に正規化を行なっています。 printLineメソッドによる実数の表示は、なんとなくお任せ的で、0が一杯になるときは、指数表現されます。そうではないときは、普通に表示されます。これを指定したい場合は、タートル・グラフィックスではformatNumberメソッドが用意されています。
		
	printLine(  45D  );		// 45.0と表示されます
	printLine(  0.00000032  );	// 3.2E-7と表示されます
	printLine(  55e+10  );		// 5.5E11と表示されます
		

■整数と実数との変換および型変換

実際の数値計算では、整数以外に実数を基本として計算することが多いでしょう。たとえば、金銭計算などの数値情報にしても、利率などを考えると実数を使わざるを得ません。これまでは、主に整数を基本として数値計算を考えてきましたが、ここでは実数を基本とした数値計算を考えましょう。まずは、整数と実数の間の変換から考えます。

☆暗黙の型変換

実数が式に現れた場合は、Javaでは評価結果は、必ず実数(しかもdouble型)になります。次の例のように、実数で答えが欲しい場合は、演算の対象となる数値を実数表記(小数点をつける)にします。
		
	例:   10/8.34   答えは実数
	     10/8     整数の除算で、答えは整数の1
	     10/8.0    実数の1.25が答え
		
実数がどのタイミングで式中に現れるかも注意しなければなりません。式の評価は、多くの場合は左から右に行われますが、優先順位にも依存します。以下の計算では、計算の途中で、実数が現れるタイミングが違いますので、最終的な計算結果が異なります。
		
	例:  10/8 * 8.0   答え:実数8.0   理由:10/8の結果は整数除算で1になるから
	    10/8.0 * 8   答え:実数10.0   理由:10/8.0の結果は、実数で1.25になるから
		
変数への代入としては、整数の式を実数型の変数に代入するときは、そのままで何もする必要はありません。次の例のように、実数に変換されて代入されます。
		
	例:  double  x = 10;		// xには10.0が代入される
		

☆明示的な型変換

逆に、実数を整数に直すような場合は、丸め(Round)を行なわなければなりません。丸めとは小数部分を取り去る操作のことを指します。そのためには、明示的な型変換が必要となってきます。まず、式の評価結果を別の型の値に変換するための書式を紹介しましょう。 長桁整数型のときに一度出てきましたが、型を別の型に変換するためには、キャスト(cast)という式が用いられます。
▼キャストの書式
 ( 変換したい型名やクラス名 ) 式
型によっては、変換できないものもあります。例えば、5という数はそのままでは文字列の"5"に変換することはできません。しかしながら、整数と実数の間ではキャストを使えばかなりうまく値を変換することができます。次の例は、幾つかの式を別の型に変換させてみたものです。

	例:
	(double)  5 / 2			// 結果は5.0 / 2となり2.5である。
	( int )  34. 1			//  結果は、34となる
	( int  )  - 45.5			//  結果は、-45となる
	( long )  45			//  結果は45Lとなる
		

☆実数から整数への型変換

上記の例にありますように、実数から整数への変換は、切り捨てという形になります。これをJavaでは「0に近くなるように丸める」と呼んでいて、整数部だけを取り出すことになります。他の言語では、truncateあるいはtrunc関数と呼ばれているものが、この操作にあたります。

実数の式を整数型の変数に代入するときは、このキャストを用いて型の強制変換を行なう必要があります。ここで、よく間違いがちなのが優先順位です。キャストは、単項演算子の一種で、掛け算や割り算などよりも高い優先順位を持ちます。そのため、次の例にありますように、括弧をうまくつけないと整数の式になってくれません。

	例:
	int z = 10/8.34;		// これは間違い、エラーになる
	int z = (int) 10/8.34;	// これも間違い、優先順位が違う
	int z = (int) (10/8.34);	// これが正解
		
ただし、実数の式の評価結果が、整数で表せる範囲を超えた場合は、エラーになります。例えば、次のような実数を整数型にして、代入するとプログラムの実行時にエラーが発生します。実行時のエラーは例外と呼ばれています。

	int  x =  (int )  45.23e56;	// 45.23 × 1056は、整数では表しきれない
		
ちなみに、以下のようにlong型の定数を整数に代入する場合は、コンパイラがチェックしてエラーにしてくれる筈(コンパイラの種類によって通ったりすることもあるので保証しません)です。

	int   x =  374838292827282L;	// この整数はint型の変数では表わしきれない
		

☆実数の入力

ターミナルやダイアログから実数値(倍精度)をユーザに入力してもらい、変数に代入するメソッドとして、 タートル・グラフィックスでは次のようなものが用意されています。
  inputNumber( )   …ターミナルからの入力
  displayInputDialog( 文字列, 実数 )   …ダイアログからのの入力

	double  value = inputNumber( );
	double  another = displayInputDialog( "Please Input Real Number", 0.1 );
		

☆実数と文字列の間の変換

タートル・グラフィックスでは、 convertToNumber( 文字列 ) メソッドで次のように、文字列を解釈して実数に変換することができます。

	double  value = convertToNumber ( "16.343e34" );
		
文字列に単に変換するには、空の文字列""と足し算するのが手っ取り早いでしょう。

	String  s = "" + 16.343e34;
		
タートル・グラフィックスでは、実数を表示する 桁数を指定する場合には、整数と同様に formatNumber メソッドが用意されています。これは、整数部の最小桁数、 最大桁数、小数部の最小桁数、最大桁数を指定してから、最後に変換する実数(または実数の式)を指定します。

	String  s = formatNumber ( 5, 5, 4, 4, 16.343 );   //  "00016.3430"
		

■実数を使った計算での注意

コンピュータは、実数に関しては概算でしか計算しません。実数を表わすビット数が限られているからです。 ここでは、誤差について説明します。「コンピュータが殊に実数に関しては、いつでも正確な計算をしない」ということを 憶えてください。

☆実数の誤差と限界

たとえば、1/3.0 * 3.0は、通常は1.0になります。 3.0で割る操作を100回繰り返して、もう一度100回3.0倍する操作を繰り返しても、理論的には同じことで結果は1.0になるはずです。 それでは実際に次のようなプログラムの断片の部分を実行させてみましょう。


	double x = 1.0;
	int n=1;
	while ( n <= 100 ) { x=x/3.0; n = n + 1; }
	while ( n > 1 ) { x= x*3.0; n = n - 1; }
	printLine( "result is "+x );
		

ところが、表示は次のようになりました。1.0にはなっていません。どうしてでしょうか?


		result is 1.0000000000000009
		

Javaでは内部的には、有限のデジタル情報として実数を表しています。 そのために、本来アナログ情報である実数を表し切れない場合があるのです。本来のアナログ情報である実数とデジタルで表した場合の実数との差を誤差(Error)と呼んでいます。 実数の計算には、誤差がつきものなのです。Javaでは、実数を64bitの高精度で表していますので、従来のプログラミング言語で頻繁に起きていた誤差がかなり少なくなっています。しかし、上のように単純な割り算・掛け算も繰り返して行なわせると誤差が入り込んできます。


	int n=100000000;
	double delta = 1.0/n;
	double result = 0.0;
	for ( inti=1; i <= n; i++ ) { result += delta }
	printLine( "result is "+result );
		

以上のプログラムは、1を1億で割ったものを、0に1億回足すプログラムになっています。 答えは、1.0になるはずですが、実際には以下のようになります。これも誤差のなせる結果です。


	result is 1.0000000022898672
	

■実数の誤差の類型

以下に誤差のいくつかの類型を見ていきましょう。

☆丸め誤差

Javaでは、有限のビット数でしか実数を表わせませんので、どこかで桁を打ち切るしかありません。このときに、行なわれる四捨五入や切り捨てなどの操作によって真の数とコンピュータ上で表された数が異なるために起こる誤差を丸め誤差(Round Error)と呼んでいます。

☆情報落ち

Javaの実数の計算の中入り込んでくる誤差を情報落ちと呼んでいます。誤差の多かった時代には、なるべく情報落ちを少なくするような形で実数の計算が行なわれていました。情報落ちが発生しやすいときはどんなときでしょうか?経験的に、次のようなことがよく言われています。
「違うオーダー(大きさ)の数を足し算したり、引き算したりすると誤差が発生する」
たとえば、非常に大きな数と小さな数を足し合わせることを考えてみてください。100,000,000に1を足すときは、100,000,000であっても100,000,001であっても、数の大きさ的には大きくは変わりません。同じように、有限のデジタル情報として実数が表されるとき、小さな差は丸められてしまいます。この丸めが情報落ちを発生する原因となっています。実数で計算を行なう場合は、このような大きさが著しく違う数を足したり、引いたりすることを避けなければなりません。

	printLine(  1.234e67 + 1.234e-45 );		// 2番目の1.234e-45が足されたことが無視されています。
		
また、float型をdouble型に変換しても、すぐに情報落ちが起こります。たとえば、プログラム上で次の表示をさせてみて下さい。

	printLine(  (double)0.3f );
		
これは、単精度実数を倍精度実数に変換しただけなのですが、表示は「0.3」ではなくて、次のようになります。 これだけみても、情報落ちが単精度実数で特に酷いのがわかると思われます。

	0.30000001192092896
		

☆桁落ち

桁落ちとは、値がほぼ等しく丸め誤差をもつ数値どうしの減算を行った場合、有効数字が減少することを指します。

★実習8-1 誤差があるのを実際に見てみよう

上記の情報落ちを、いくつかの計算で確かめてみましょう。クラス名は、Practice0801とします。

☆打ち切り誤差

無理数の√2や、πなどを考えてみてください。Javaでは、有限のビット数でしか実数を表わせませんので、どこかで計算を打ち切るしかありません。このようにして入り込んでくる誤差を打ち切り誤差(Drop Error)と呼んでいます。以下のJavaのプログラムの断片は、それらを表示するものです。詳しい内容は、次の節で説明します。ここで表示された範囲だけしか、計算に入れていません。それ以下の小数部は無視されています。

	printLine(  Math.PI );
	printLine(  Math.sqrt( 2.0 ) );	
		
MacintoshのJDK 1.5.0の環境では、以下のように表示されました。だいたい17桁程度です。

	3.141592653589793 
	1.4142135623730951
		

★実習8-2 numberFormatを使ってみよう

上記の表示は標準の桁数しか表示されません。そこで、繰返しでnumberFormatメソッドを使って小数部が どこまで詳しく表示されるかどうか見て下さい。クラス名は、Practice0802とします。

☆誤差を考慮してif文の条件式を作る

上記の誤差について結論を述べると次のようなことになります。

コンピュータは実数に関しては、必ずしも正確な計算をするとは限らない。
プログラマが注意してなるべく、本来の値(真数)に近づくように計算をさせる必要がある。

たとえば、誤差を含んだ実数において、「等しい」というのは、機能しない場合があります。 次のような条件式を持ったプログラムについて考えてみましょう。xは実数型の変数とします。

		
	if ( x == 10.3 ) { ..... }
		

このような記述は、ほとんどの場合うまく機能しません。誤差が含まれるので、10.3の値になる可能性が 少ないからです。実数の場合には、等価性を求めるときには「ある範囲内に入っている」という記述をします。 たとえば、上記のような場合は、一定の誤差が含まれる範囲を指定しておき、その範囲内に入っていたら、 同じであると見なすという形で記述します。

		
	double  range = 0.01;			// 誤差の許容範囲を0.01としています。
	if ( x >= 10.3 - range  && x <= 10.3 + range ) { ..... }
		

8−2.関数メソッドの利用

■Mathクラスについて

Mathクラスには、数学上使う便利な関数がメソッドとして用意されています。ここでは、最低限の三角関数と その他の良く使うものだけに限り紹介します。

定数またはメソッド評価結果の型意味
PIdouble 円周率を表す
cos( 角度 )double cosを求める
sin( 角度 )double sinを求める
tan( 角度 )double tanを求める
acos( 底辺/斜辺 )double 底辺と斜辺の比から角度を求める(値域は0〜π)
asin( 直立辺/斜辺 )double 直立辺と斜辺の比から角度を求める(値域は-π/2〜π/2)
atan( 直立辺/ 底辺 )double 直立辺と底辺の比から角度を求める(値域は-π/2〜π/2)
atan2( 実数 , 実数 )double 2つの実数をy、x座標として角度を求める
abs( 数 )double, float, long, int 絶対値を返してくる
pow( 実数, 実数 )double べき乗を求める
random( )double 0.0以上1.0未満の間で乱数を返してくる
sqrt( 実数 )double 平方根を求める

実際にこれらの関数を使って簡単な数式、あるいは代入文を記述してみましょう。

☆三角関数、逆三角関数

三角関数、逆三角関数の場合は、角度を指定するときはすべてradian体系(πを基準とする体系)で行なわれます。通常の360度を使って角度を表す体系はdegree体系と呼ばれています。その間の変換は、次のようになります。なお、πを指定するときは、JavaではMath.PIと記述します。
radian角度 = degree角度 / 180 * π
例えば 90゜は1/2π
代表的な角度を以下に書き記してみました。
degreeradiandegreeradian
00180π
45π/ 42703π/ 2
90π/ 2360
これを変換するために、Java2からは、以下の2つのメソッドが用意されました。計算結果は実数です。
 Math.toRadians( degree角度 )    … radian角度に変換する
 Math.toDegrees( radian角度 )    … degree角度に変換する

実際に、三角関数などを使った式を記述してみましょう。


	double  radians = Math.toRadians( 60 );  // 60度をradian角度に変換する
	double  bottom = Math.cos( radians ) * 10;  // 60度のときの斜辺が10のときの底辺の長さを求める
	double  result = Math.sin(  Math.toRadians(  60 )  );		//  sin(  60  ) = √3 / 2 を求める
	double  ratio  =  Math.cos(  Math.PI / 3 );			// cos  60 = 1/2 これは斜辺の長さが1のとき
	double  alpha =  Math.toDegrees(  Math.atan2( 1.0,  1.0 )  );			// x, yの値から角度を求める。alpha = 45度
		

☆課題8-1 三角関数で距離を求める

ユーザに三角測量のときに、垂直に立てた測定物差しまでの距離aと、 本当に測定したいものと測定物差しの間の角度θを入力してもらいます。 そして、本当に測定したいものとの距離rを求めるようなプログラムを書いて下さい。クラス名は、 Triangulationです。え?忘れたって?斜辺をrとして、底辺をaとするとcosθ=a/rでしたね。 さて、余裕がある人は、上記の場合は測定したいものと丁度90度を成す角度の位置に測定物差しが 置かれていたのですが、測定物差しの場所からみて、測定者と測定対象の角度がφ度だったときに、 測定者と対象物までの距離rを求めるプログラムを書いてみて下さい。

☆その他の関数

その他の関数としては、絶対値を求めるようなもの、自然対数関連のもの、べき乗や平方根などがあります。絶対値以外は、実数に対してしか用意されていないことに注意してください。

	int        abs = Math.abs( -44 );				// 44
	double  power = Math.pow( 4, 3 );			//  43
	double  rand = Math.random( );			//  0.0 <= n < 1.0
	double  root = Math.sqrt( 2.0 );				//  √2 = 1.41421356....
		
Math.randomは、乱数になっていて、0.0〜1.0の間の適当な実数を発生して返してくれます。しかし、1.0という値は発生しませんので注意が必要です。たとえば、サイコロのように1から6までのどれかの整数を得たい場合は、次のように記述します。

	int   dice =  (int)( Math.random( ) * 6 + 1);			//  1〜6までの乱数を発生
		
タートル・グラフィックスでは、これ以外に乱数を発生させる関数をいくつか用意しています。乱数の利用の項目で説明します。

☆課題8-2 ピタゴラスの定理から距離を求める

2つの垂直に交差する軸の距離から、2点間の距離を求めて下さい。 r2=x2+y2でした。クラス名は、Pythagorasです。

■実数を使った数値計算

☆簡単な積分の計算

3角形の面積を求めてみましょう。もちろん、(幅+高さ)/2で求められますが、これをコンピュータを使って 求める形にします。ということで、下記の図のようなy = xという直線を考えます。これを、xが0からxnまでについて、 n個に分割して、等間隔h(=xn/n)に区切って求めます。積分を高校でやった人は「台形公式」などと呼ばれて習っているかも知れません。 下記の式でy0は、x = 0のときのyの値で、ynは、xn のときのyの値です。

 面積 = ( y0 + 2 * ( y1 + y2 + … + yn-1 ) + yn ) * h / 2

下の図では、xn=2で、nは8です。赤い垂直線の横の間隔がh(=0.25)になっています。

プログラムは、startメソッドだけを記述しています。x=0からx=2まで、hは0.1になっています。

		
	public void start( ) {
		double x = 0;		// xの初期値
		double y = x ;		// x = 0のときのyの値
		double area = y;	// 面積に足し込んでいます。
		double limit = 2;		// xの上限値
		int n = 20;		// 分割数
		double h = limit / n;		// xの間隔
		for ( int i = 1;  i < n; i = i + 1 ) {
			x = x + h;		// xに間隔を足し込む
			y = x;			// そのときのyの値を求める
			area = area + 2 * y;	// 面積に足し込む(台形公式から2倍して)
		}
		// 最後のyの値を足し込む
		x = x + h;  // 上限値のxの値にするために間隔を足す。
		y = x;		// 上限値のときのxの値からyの値を求める
		area = area + y;		// それを面積に足し込む
		area = area * h / 2.0; 		// 台形公式から最終的な面積を求める
		printLine( area );
	}
		

この台形公式を使うと、yが曲線になっていた場合、求める区間の曲線の形が凸型だと、実際の面積よりも小さくなってしまいます。 また、求める区間の曲線の形が凹型だと、実際の面積よりも大きくなってしまいます。それを少し補正した 「シンプソンの公式」などが有名です。 それ以外にも求める区間の面積(それが積分値なのですが)を求める 区分求積法はいろいろとあります。

★実習8-3 y = x2の面積を求める

上記のプログラムは、y = x の面積を求めるものでした。これを少し改良して、y = x2で、 xが0から1までの面積を求めてみてください。 ちなみに、積分の公式からすると、S = (1/3.0) * (13)で求まりますが、どれだけこの値に近づけるか、 分割の幅を細かくしてみてください。また、右隣のy=-x2+2xの面積も、xが0から2の区間の間で求めて 見てください。これも積分の公式からすると、S = - (1/3.0)*(23)+22で求まります。 クラス名は、Practice0803で。
 

☆課題8-3 円の面積を求める

円の面積は、半径をrとするとπr2で求められますが、これを上記の方法で求めてみましょう。 y = √(r2 - x2)でしたね。これを分割して、xが0からrまでについて求めます。 その結果を4倍します。さて、その結果がπr2にどれだけ近づけるか、分割の幅を細かくしてみて 下さい。たとえば、rを1にして計算してみましょう。クラス名は、AreaOfCircleにて。 注意したいのは、平方根の引数は、マイナスにはなってはならないということです。 実行時のエラーが出て止まってしまいます。 上記のx = x + h;の式で、xを0から1まで変化させた場合、最後のxは、1.0にはなってくれません。 平方根を求める際に、xの値が1以上になってしまい、マイナスの値が出てしまう場合は、 if文でyに0を入れるように対処しておきましょう。


8−3.等比数列と利息計算

社会系の学問でも実数を使った様々な計算が行なわれますが、ここでは一番身近な金利の計算を実数を用いて行なってみましょう。

■等差数列と等比数列

次の小節で出てくる複利計算のように、一定の率で、掛け算されていくような数列を等比数列(Geometric Progression)と呼びます。一定の差が足されていく等差数列(Arithmetical Progression)と比べてみます。初項をa、公差をd、公比をrとすると、それぞれ次のようになります。
 等差数列の第n項  an = a + ( n -1 ) * d;
 等比数列の第n項  an = a * r (n - 1)
例をあげてみましょう。たとえば、a = 1、 dおよびrを両方とも2と置くと次のようになります。

		等差数列	1	3	5	7	9	11	13	15
		等比数列	1	2	4	8	16	32	64	128
		
数列にはいろいろな公式がありますが、等差数列も等比数列も、while文などの繰返しを使えば、直感的に求めることができます。たとえば、次のプログラムの断片は、第10項までの等差数列の総和を求めています。
		
	int  total = 0;
	int  a = 1,  d = 2;
	int i = 1;
	while ( i < =10 ) {
		total  =  total + a;
		print(   "第" + i  + "項は "  +   a  +  "       "  )
		a = a + d;
		i = i + 1;
	}
	printLine(  );
	printLine( "ここまでの総和は" + total );
		

★実習8-5 等差数列の等比数列の総和を求める

上記のプログラムの変数aと変数dの部分をユーザに入力してもらうようにします。また、上記の場合は、等差数列の総和を求めていますが、 等比数列に変えて実行してみてください。クラス名は、Practice0805とします。

なお、等差数列も等比数列も第n項までの総和を求める公式があります。 等比数列は、次の金利計算のところで紹介します。等差数列は、以下のようになります。

 total = n * ( 2 * a + ( n - 1 ) * d ) / 2    初項と公差から
 total = n * ( a + z ) / 2          初項と末項から( z = an )

■金利計算の基礎

実数を使って金利計算するときは、利息(Interest)を計算することが主体になります。利息を求めるのは等比数列の計算をすることい他なりません。この利息をどのように計算するか、いつ利息を発生させるかによって計算方法が異なってきます。

☆単利法と複利法

定期預金やローンなどの利息を計算するときには、複利法(Compound Interest)を使います。元金に利率を掛けた結果が利息になりますが、単利法(Simple Interest)で利息を計算する場合と複利法で利息を計算する場合は、期間が複数に渡る場合には利息の額が異なってきます。
 単利法…元金に期間数と利率を掛けたものが利息になります
 複利法…期間毎に元金と利息の合計に利率が掛かかったものが次の利息になります
それでは、例えば定期預金で10,000円を3年預けた場合を考えてみましょう。年利率1%とします。3年後に額はいくらになるでしょうか?

	単利法…10000  + 10000 * 0.01 * 3  →  10,300円
	複利法…(10000 * (1 + 0.01)) * (1 + 0.01 ) *  (1 + 0.01)   →   10,303円
		
単利法と複利法では以上のように差がでてきます。利息の計算は通常は複利法で計算します。複利法の場合、貯める場合はより利息が大きくなります。逆にお金を借りる場合は、最初の利息が高くなります。また、数学的には複利法での計算は、等比数列の計算の応用となっています。

☆年率、月率、日率

通常利息を使われるの年利率(年率あるいは年利:Annual Rate)は、1年における利率を示しています。しかし、月で返済するようなローンや、日で返済するようなローンなどの場合は、1月の利率(月率)、あるいは1日の利率(日率)を求める必要があります。さらに、複利を使った定期預金でも、半年複利のものもありますし、ローンでもボーナス払い(年に2回と考えます)を別に用意しているものもあります。その場合には、半年の利率が利息計算に使われることになります。
 半年の利率   → 年率を2で割ればよい
 月率      → 年率を12で割ればよい(注1)
 日部(ひぶ)  → 年率365(366)で割ればよい
「金利」という言葉は通常は年率を示しています。注意しなければならないのは、消費者金融などで使われている実質年率と名目年率(約定年率)の違いです。
 名目年率…預金金利や貸出金利のように契約で定められている年利率を指します。
 実質年率…名目年率から物価上昇率(インフレ率)を差し引いた年利率です。
たとえば、名目年率が30%でも、その年の物価上昇率が2%であった場合には実質年率は28%になります。法律的には、年率は15%〜20%(金額による)を上限としています。消費者金融の実質年率が28%と表記されていましても、名目年率はそれよりもたぶん30%ですので実生活でも注意したいものです。ところで、法律的に決められた上限以上を支払う必要はありません。

☆固定金利と変動金利

利率が一定の場合は、固定金利と呼びます。それに対して、変化するものを変動金利と呼びます。変動金利は、1年毎に利率が見直されるものと半年毎に利率が見直されるものとがあります。
 固定金利      年利率が一定
 変動金利      年利率が相場に応じて変わる
 上限つき変動金利  変動金利だが、上限(これ以上高くならない限度)はある

■預入れの金利計算をしてみる

☆定期預金の場合

それでは、定期預金を想定して、10000円を預けて、固定金利で、年利率が4.4%の場合(注2)、10年でいくらになるか、毎年表示してみましょう。毎年結果として求まる額を終価と呼びます。変数baseには元金が、変数totalには終価が代入されます。また変数rateは、年利率が代入されています。totalとrateは、実数型の変数になっていることに注意してください。以下は、標準のJavaのアプリケーションとして記述しています。手で入力される人は、「System.out.println」の最後の「println」は、「printLine」の略なので小文字のエル(l)です。大文字のアイ(I)と区別がつきにくいので注意してください。

	public  class  Financial {
		public  static  void  main( String [] arg ) {
			intbase = 10000;			// 元金
			double total = base,   rate  = 0.044;		// 終価と年利率

			int year = 1; 
			while  ( year <= 10 ) {
				total  =  total * (1+rate);
				System.out.println(  year + "年後は、" + total + "円" );
				year = year + 1;
			}
		}
	}
		
なお、等比数列の公式を用いれば、繰返しを使わなくとも、次の公式で10年後の終価を計算することができます。なお、Math.powerの引数は両方とも実数なので、10年の場合は、10.0と記述した方が良いかも知れません。
 double total = base * Math.pow( 1+利率, 年数 ) ;

※注1 正確には、月率は各月で日数が違うので日率をその月の日数分合計したものになります。ここで12で割っているのは概算で求める場合、あるいは月率を一定にする場合に使われる計算方法です。
※注2 こんなに高い預金金利は不況下の日本では外国債ぐらいしかありません。でも、金融破綻もありましたし、外国債も為替変動の 影響を受けるのでリスクが高いことを承知していないといけません。。

☆課題8-6 ユーザの入力で定期預金を計算する

元金と、年利および、何年間預けるかをユーザに入力してもらい、 毎年どれくらい預金が増えていくかを表示するプログラムを作りなさい。 クラス名は、TimeDepositで。

☆年金計算

ここで言う「年金」とは老後のための積立金のことではありません。財務的には広く定期的にお金を積み立てたり、払ったりするもののことを年金と呼ぶのです。貯める場合と返済する場合で年金の呼び方が違います。
貯める場合の年金…積立金(Deposit)
返済する場合の年金…賦金(ふきん:Installment)

☆期末払いと期首払い

積立金では、期首払い(それぞれの期の最初で年金を払う)場合と期末払い(それぞれの期の最後で年金を払う)場合では、利息が微妙に異なってきます。
期首払い…年金にも利息がつく
期末払い…年金には利息が付かない
たとえば、1万円の元金があって、年利率0.2%で毎年1000円の積立金であるような預金の場合に、期首払いでは1年後は11,022円ですが、期末払いでは11,020円となります。2円の差は、期首払いの場合に年金にも利息が付いたからです。賦金の場合は、期末払いがほとんどですが、その期に借りている額に対して利息が発生します。

☆定期積立預金(Installment Savings)の場合

元金として10,000円を預けて、固定金利で年利率が5.5%の場合、毎年5000円を預けていくと、10年でどれくらいになるか終価を表示してみましょう。積立金は期末払いとします。
		
	public  class  Saving {
		public  static  void  main( String [ ] arg ) {
			int base = 10000, deposit = 5000;	// 元金と積立金
			double total = base,   rate  = 0.055;		// 終価と年利率

			int year=1; 
			while  ( year <= 10) {
				total  =  total * (1+rate) + deposit;
				System.out.println(  year + "年後は、" + total + "円" );
				year = year + 1; 
			}
		}
	}
		
なお、等比数列の和の公式を用いれば、次の式で10年後の終価を求めることができます。
 double total = base * Math.pow( 1+利率, 年数 ) + deposit * (Math.pow( 1+利率, 年数 ) - 1) / 利率;

☆課題8-7 ユーザの入力で積立型定期預金を計算する

元金と、年利および、毎年の積立金、何年間預けるかをユーザに入力してもらい、毎年どれくらい 預金が増えていくかを表示するプログラムを作りなさい。 クラス名は、RegularSavingDepositで。

☆ExcelでFV関数を使って計算してみる

Excelでは、FV (将来価値:Future Value)関数が用意されていて、 指定された利率と、期間数に基づいて投資の将来価値と一連の追加支払いを計算できます。
形式 FV (利率,期間数,定期支払額 {,現在価値}{,期末期首})
引数
・利率:一定期間の利率。
・期間数:支払い回数。
・定期支払額:期間ごとの支払額。
・現在価値:オプション。現在の投資額。
・期末期首:オプション。支払方法。0または1になります。各期間の期末に支払う場合は0を、期首に支払う場合は1を指定します。省略された場合は、0(期末払い)
	例
	=FV (0.09,12,-100,-3000) 		結果=¥10452.07 (通貨書式を使った場合)
		
この例では、¥3,000の投資の将来価値を示し、今後12年にわたり毎年¥100の投資を続ける場合、年率0.09(=9%)とすると、12年後にはこの投資の価値は¥10452.07になると、計算されています。

★実習8-6 為替変動のリスクを承知での外国債預け入れのとき

Excelを使います。これは、元金に利率を掛けるだけの場合です。100,000円を預けて、 固定金利で、年利率が4.4%(=0.044)の場合、10年でいくらになるか、毎年表示してみてください。 また、預け入れた額と総額の差(利息)も表示してみてください。クラス名はPractice0806

★実習8-7 定期預金積み立てのとき

Excelを使います。これは、元金に利率を掛けるだけの場合です。100,000円を預けて、毎年5,000円ずつ積み立てして、期末で 固定金利で、年利率が0.24%(=0.0024)の場合、10年でいくらになるか、毎年表示してみてください。 また、預け入れた額と総額の差(利息)も表示してみてください。

■賦金計算

借入金を返済する方式には大きく分けて次のような2つの方式があります。それぞれの場合に、返済のイメージが異なります。一般の賦金の場合には、元利均等方式が用いられています。これを使って返済について計算してみましょう。
 元金均等方式 … 月々決まった額の元金に利息を加えた金額を返済する方式
 元利均等方式 … 借入元金と借入利息を合わせて月々、同じ金額を返済する方式

☆ローンの返済に何カ月掛かるか?

実際に賦金の返済をプログラミングしてみましょう。たとえば、無謀にも300万円のBMWを買ってしまったとします。頭金が100万円として、固定金利で年利率が14.5%のときに、元利均等返済で月々50000円ずつ期末払いで払っていくと、何カ月で返せるか計算してみましょう。支払った月数と、残金を表示していきます。totalが毎回の返済残高、rateは12で割っていますので月率を表し、loanが月々の支払額を示しています。
		
	public  class  Installment {
		public  static  void  main( String [] arg ) {
			int	left = 3000000-1000000, loan = 50000;	// 借入金と月々の支払額
			double	total = left,   rate  = 0.145/12;	// 支払い後の残金と月の利率

			int  month = 1;
			while  (  total > 0 ) {
				total  =  total * (1+rate) - loan;
				System.out.println(  month + "月後の残金、" + total + "円" );
				month = month+1;
			}
		}
	}
		

☆課題8-8 ユーザの入力でローン返済月数を計算する

借金と、年利および、毎月の返済金をユーザに入力してもらい、 何回で支払いができるのか表示するプログラムを作りなさい。 上記のプログラムのように何月後の残金も表示しなさい。 なお、支払いきれないときも表示しなさい。 クラス名は、MonthlyInstallmentで。

☆ExcelでNPER関数を使って計算してみる

Excelでは、NPER(支払期間)関数が財務関数と用意されていて、 利率が一定であると仮定して、継続的なキャッシュフロー(投資と収益)から投資に必要な期間を計算します。
形式 NPER (利率,定期支払額,現在価値 {,将来価値} {,期末期首})
引数
・利率:一定期間における利率。
・定期支払額:一定期間毎の支払額。
・現在価値:投資額。
・将来価値:オプション。投資の将来価値または最終の支払後に残る現金価値。省略した場合、将来の価値は0になります。
・期末期首:オプション。支払方法の種類。各期間の期末に支払う場合は0を、期首に支払う場合は1を指定します。初期設定は0です。

	例
	=NPER (0.1/12,-25000,1000000)		結果=48.86 (少数点以下2桁の固定数値設定)
		
この例では、¥1,000,000の車を年率10%で購入する際、頭金が無しで月々¥25,000づつ支払う場合、49ヶ月の支払い期間が必要があると計算されています。0.1(=10%)というのは、12ヶ月で10%という意味です。

★実習8-8 ローン返済の場合(何カ月掛かるか?)

Excelを使います。3,000,000円のBMWを無謀にも買ってしまいまいsた。固定金利で年利率が14.5%のときに、元利均等返済で月々いくらずつ払っていくと、何カ月で返せるか計算してみます。支払額を、30000円、40000円、50000円などと変えて、支払った月数を表示していきます。期末払いです。なお、支払った総額と元々借り入れた額の差額(ローン会社が貰う利息)も表示しましょう。

☆ローン返済に月々いくら必要か?

300万円のBMWを買おうと計画しました。幸い?頭金は50万円はあるとします。残りを5年ローン(つまり60回払い)で買いたいときに、元利均等返済で、利率は固定金利・複利で年利率14.5%で、期末払いという条件で、一体月々幾ら払えば良いのでしょう?外側の繰返しで、月々払うお金を10000円から始めて、100円単位で上げていき、払いきれるかどうか試行しています。内側の繰返しでは60回分払ってみています。60回払った後の残高を表示していきます。もし、60回後に残高が0円よりも小さくなっていたら、月々その金額で払えば払い切れることになります。
		
	public  class  Loan {
		public  static  void  main( String [ ] arg ) {
			int	base = 3000000-500000;
			double	total=base,   rate  = 0.145/12;

			int  loan = 10000 ;
			while ( total > 0 ) {
				total = base;
				 int  month = 1; 
				while  (month <= 60 ) {
					total  =  total * (1+rate) - loan;
					month = month+1;
				}
				System.out.println( "月々の支払:" + loan + " 60回後の残金: "+ total  );
				loan = loan + 100;
			}
		}
	}
		
月々の支払額を求めるには等比数列の和の公式を使えば、上のような繰返しを使わなくとも、等比数列の公式から、次のような式で求めることができます。また、利息の総額を支払総額から元金を引くことにより求めることができます。ただし、下記の公式の利率は年率なので、月賦の場合は、12で割る必要があります。
 double loan = base * Math.pow( 1+利率, 回数 ) * 利率 / ( Math.pow( 1+利率, 回数 ) - 1 ); // 毎回の支払額
 double interest = loan * 回数 - base; // 総支払いに占める利息額

☆課題8-9 ユーザの入力で元利均等返済で月々のローン支払いを計算する

借金と、年利および、何回支払いをユーザから入力してもらい、月々の支払いが どのくらいかを表示するプログラムを作りなさい。 上記のプログラムは100円単位で計算してますが、1円単位で表示しなさい。 なお、支払いきれないときも表示しなさい。 クラス名は、PayMonthTransferで。

☆ExcelでPMT関数を使って計算してみる

ExcelのPMT(支払額)関数は、指定した期間、定額の支払いを行い、その期間の利率を一定と仮定して、投資に必要な定期支払額を計算します。
形式 PMT (利率,期間数,現在価値 {,将来価値} {,期末期首})
引数
・利率:一定の期間における利率。
・期間数:投資期間を通じての支払回数。
・現在価値:現在の投資額。
・将来価値:オプション。投資の将来価値または最終支払後に残る現金価値。何も入力されていない場合、0になります。
・期間期首:オプション。支払方法。0か1になります。各期間の期末に支払う場合は0を、期首に支払う場合は1を入力します。何も入力されていない場合、0になります。

	例
	=PMT (0.1/12,36,1200000,200000)		結果=-¥33934 (通貨表示に設定されている場合)
		
この例では、¥200,000の頭金、年率0.1(=10%)、支払い回数36回で¥1,200,000の車を購入した場合、毎月の支払い額は¥33934になると計算されています。引数の0.1/12は、12ヶ月で10%という意味です。

★実習8-9 ローン返済の場合(月々いくら必要か?)

3,000,000円のBMWを買おうと計画しました。全額を分割払いで買いたいと思っています。Excelを使って、元利均等返済で、利率は固定金利・複利で年利率14.5%とします。一体月々幾ら払えば良いのでしょう?支払い回数を変えて、月々の支払額を計算してみて下さい。なお、支払った総額と元々借り入れた額の差額(ローン会社が貰う利息)も表示しましょう。

8−4.乱数の利用

■タートル・グラフィックスでの乱数のメソッド

整数の値として、適当な値を発生させることはシミュレーションなどではよく必要とされます。 そのような適当な値のことを乱数(Random Number)と呼びます。 発生度合いの分布が、最小値から最大値までの一様に起こりえる分布を持つ乱数のことを一様乱数、 正規分布に基づいて、中心の値が割と多く発生する乱数のことを正規乱数と呼びます。 Javaでは、一様乱数を発生させるのが標準となっています。

Javaの標準ライブラリ(JFC)で用意されている乱数は、先に紹介したMath.random( )という メソッドです。これは、0.0以上、1.0未満の値で一様乱数の実数を返してきます。 通常は、このメソッドに掛け算や足し算あるいは引き算などを利用して、目的の範囲にあった 乱数を作り出します。

タートルグラフィックスでは、簡単に目的の範囲の一様乱数を作り出すために、次のような メソッドが用意されてます。randomIntegerだけは、最大値で指定された数も返すので注意して下さい。

 random( 最大値 )  … 実数で0以上最大値未満の乱数を返します。
 randomNumber( 最小値, 最大値 )  … 実数で最小値以上最大値未満の乱数を返します。
 randomInteger(最小値,最大値 )  … 整数で最小値以上、最大値「以下」の乱数を返します。

上記の表記で、randomIntegerメソッドのパラメータで指定されている最小値や最大値だけは、整数型の式です。 他のメソッドは、実数型でも整数型でも構いません。 これらのメソッドを使えば、たとえば、次のように記述できます。標準のMath.randomを使ったときに、 同じように記述するにはどうしたらよいか、コメントに示しました。

		
	int  dice = randomInteger( 1, 6 );		// (int)(Math.random( ) * 6 + 1 )
	int  fifty = randomInteger( -50, 50 );		//  (int)(Math.random( ) * 101)  - 50
	double  range = random( 365.2522 );		//  Math.random( ) * 365.2522
	double   bound = randomNumber( -10.2, 5.6 );		//  Math.random( ) * (5.6 + 10.2 ) - 10.2
		

★実習8-10 大数の法則

上記のさいころの目を繰返しを用いて、30回発生させて見なさい。そして、「count1」から「count6」までの 変数を用いて、何回その目が発生したかをカウントするようなプログラムを作りなさい。 カウントされた6つの目の値を最後にターミナルかダイアログに出力しなさい。 Practice0810という名前でクラスを作ります。 30回を3000回、300000回と変えて見なさい。だんだん、発生する個数が一様になってきます。 このように乱数が発生する回数が高ければ高い程、分布が乱数本来の分布に近づくことを統計学では 「大数の法則」と呼んでいます。

■モンテカルロ法

モンテカルロ法(Monte Carlo method)は、最初の回で出てきたフォン・ノイマンが考えだした、 乱数を用いていろいろなシミュレーションを行なう手法のことを指しています。

★実習8-11 よたつく亀、戻ってくるかどうか

最初に1回だけ、亀を横(右方向へ90度)に回します。 -5から5までの乱数を発生させ、それを亀を回す方向。亀を1ドット進めます。 乱数を発生させて、亀を進めるのを回数繰返し文で繰返して、亀がどこに向かうか見てみて下さい。 たとえば、100回・500回・800回・1000回などで繰返しの回数を変えて、実行してみましょう。 一様乱数で、大数の法則が効いてくれば、亀は水平(右方向へ90度)へ戻ってくる筈なのですが、、、、 結果は見てのお楽しみ。また、乱数を発生させる範囲を-3から3までや、-1から1までとして、 その違いについても考えて見なさい。 クラス名は、Practice0811です。

★実習8-12 乱数で上下左右にタートルを動かす

200回ぐらいの繰返しで、整数の乱数で前か左右に行くことを決めて、更に実数の乱数で進む距離 (例えば5〜15ぐらいの範囲)を決めるプログラムを書いて下さい。クラス名は、Pratice0712です。

☆課題8-10円内を動き回るタートル

ピタゴラスの定理を利用して、一定の円の中で動き回るタートルを作ってみて下さい。円の中心からの距離が、 半径を超えたら、適当な角度(例えば160〜200度の範囲内で乱数で決めて)で、反射するような形で、 タートルを動き回らせます。タートルのx座標の整数値は、getIntegerX( )で、タートルのy座標の整数値はgetIntegerY( )で求める ことができます。最初のx座標とy座標を記憶しておきます。動き回っている最中のx座標とy座標と最初の 座標の差分から距離を求めます。クラス名は、CreepWithinCircleです。

☆課題8-11花びらの枚数が異なる花を描くタートル

整数の乱数で、3〜8までの値を発生させ、値に応じて花びらが3枚から8枚までの花を描くような メソッドを定義して、そのメソッドを呼出しなさい。実際に、花が描けているかどうか、まず繰返しで 呼び出して確かめなさい。加えて、乱数で発生させて、横一列に様々な枚数の花びらを持つ花が出るように しなさい。 ちなみに、120度円弧を描き60度回転して120度円弧を描くような花びらを描く場合は、 花びらの枚数をm枚とすると、毎回花びらを描いた後、60-360/m度回転するとうまく描けるようです。 クラス名は、DrawRandomFlowerとします。

☆課題8-12花びらの膨らみが異なる花を描くタートル

整数の乱数で、3〜9までの値(ただし7を除く)を発生させ、値に応じて花びらの膨らみが異なる ような花を描くようなメソッドを定義して、そのメソッドを呼出しなさい。実際に、花が描けている かどうか、まず繰返しで呼び出して確かめなさい。加えて、乱数で発生させて、横一列に様々な枚数 の花びらを持つ花が出るようにしなさい。 ちなみに、花びらの枚数が6枚の場合は、次のようにして、円弧を描き回転し、再び円弧描き回転させると うまく描けるようです。クラス名は、DrawSwellFlowerとします。

	void drawSwellPetal( Turtle t, int swell, int step, int reduce ) {
		for ( int i=0; i < 6; i++ ) {
			drawArc( t, step, swell*15, reduce );
			t.rotate( 180-swell*15 );
			drawArc( t, step, swell * 15, reduce );
			t.rotate( 120-swell*15 );
		}
	}
		


<<Previous Lecture >>Next Lecture