Home >> Lecture 8
実数は、内部的には国際規格のIEEE-754と呼ばれる浮動小数点表現の規格に基づいて表現されています。floatの方は、32ビットで表現されていますし、doubleの方は64ビットで表現されています。上の範囲をみればわかるように、double型の方が、より精密な数、より大きい数を表すことができます。 通常に実数の定数を書きますと、double型の定数であると解釈されますが、明示的に型の指定をすることもできます。実数定数の最後に「f」か「F」を付けると強制的にfloat型になります。同様に、実数定数の最後に「d」か「D」を付けると強制的にdouble型になります。
単精度実数 float 1.40129846432481707×10-45〜3.40282346638528860×10+38 倍精度実数 double 4.94065645841246544×10-324〜1.78769313486231570×10+308
型を明示した実数定数の例: 3.141459F 1.603e-45f 33.1d 0D 3F 小数点の反対側を省略した例: -.05 34.
指数表現を使った実数の例: 45.6e9 3.14159E-19 5.5e+4指数表現とは何でしょうか?知らない人のために、簡単に説明します。指数表現とは、以下のように、基数(ここでは10)の何乗というものを掛け合わせた実数の表現形式のことで、非常に大きい数や小さい数などを表現するときに用います。
たとえば、「1.23×108」という表現ですが、この「1.23」の部分を仮数(Mantissa)と呼び、「8」の部分を指数(Exponent)と呼びます。また、このように0が少なくなるように指数表現を使って桁あわせを行なうことを、実数の正規化(Normalization)と呼んでいます。IEEE-754規格で表現されているJavaの実数定数でも、限られたビットサイズで、非常に小さな数から大きな数を表すために、内部的に正規化を行なっています。 printLineメソッドによる実数の表示は、なんとなくお任せ的で、0が一杯になるときは、指数表現されます。そうではないときは、普通に表示されます。これを指定したい場合は、タートル・グラフィックスではformatNumberメソッドが用意されています。
通常の書き方 指数表現 Javaでの指数表現 0.00000567 → 5.67×10-6 → 5.67e-6 123000000 → 1.23×108 → 1.23e8
printLine( 45D ); // 45.0と表示されます printLine( 0.00000032 ); // 3.2E-7と表示されます printLine( 55e+10 ); // 5.5E11と表示されます
例: 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が代入される
▼キャストの書式型によっては、変換できないものもあります。例えば、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 );
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)と呼んでいます。
「違うオーダー(大きさ)の数を足し算したり、引き算したりすると誤差が発生する」たとえば、非常に大きな数と小さな数を足し合わせることを考えてみてください。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
上記の情報落ちを、いくつかの計算で確かめてみましょう。クラス名は、Practice0801とします。
printLine( Math.PI ); printLine( Math.sqrt( 2.0 ) );MacintoshのJDK 1.5.0の環境では、以下のように表示されました。だいたい17桁程度です。
3.141592653589793 1.4142135623730951
上記の表示は標準の桁数しか表示されません。そこで、繰返しでnumberFormatメソッドを使って小数部が どこまで詳しく表示されるかどうか見て下さい。クラス名は、Practice0802とします。
上記の誤差について結論を述べると次のようなことになります。
コンピュータは実数に関しては、必ずしも正確な計算をするとは限らない。
プログラマが注意してなるべく、本来の値(真数)に近づくように計算をさせる必要がある。
たとえば、誤差を含んだ実数において、「等しい」というのは、機能しない場合があります。 次のような条件式を持ったプログラムについて考えてみましょう。xは実数型の変数とします。
if ( x == 10.3 ) { ..... }
このような記述は、ほとんどの場合うまく機能しません。誤差が含まれるので、10.3の値になる可能性が 少ないからです。実数の場合には、等価性を求めるときには「ある範囲内に入っている」という記述をします。 たとえば、上記のような場合は、一定の誤差が含まれる範囲を指定しておき、その範囲内に入っていたら、 同じであると見なすという形で記述します。
double range = 0.01; // 誤差の許容範囲を0.01としています。 if ( x >= 10.3 - range && x <= 10.3 + range ) { ..... }
実際にこれらの関数を使って簡単な数式、あるいは代入文を記述してみましょう。
定数またはメソッド 評価結果の型 意味 PI double 円周率を表す 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角度 = degree角度 / 180 * π代表的な角度を以下に書き記してみました。
例えば 90゜は1/2π
これを変換するために、Java2からは、以下の2つのメソッドが用意されました。計算結果は実数です。
degree radian degree radian 0 → 0 180 → π 45 → π/ 4 270 → 3π/ 2 90 → π/ 2 360 → 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度
ユーザに三角測量のときに、垂直に立てた測定物差しまでの距離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までの乱数を発生タートル・グラフィックスでは、これ以外に乱数を発生させる関数をいくつか用意しています。乱数の利用の項目で説明します。
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が曲線になっていた場合、求める区間の曲線の形が凸型だと、実際の面積よりも小さくなってしまいます。 また、求める区間の曲線の形が凹型だと、実際の面積よりも大きくなってしまいます。それを少し補正した 「シンプソンの公式」などが有名です。 それ以外にも求める区間の面積(それが積分値なのですが)を求める 区分求積法はいろいろとあります。
![]() |
![]() |
円の面積は、半径を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を入れるように対処しておきましょう。
等差数列の第n項 an = a + ( n -1 ) * d;例をあげてみましょう。たとえば、a = 1、 dおよびrを両方とも2と置くと次のようになります。
等比数列の第n項 an = a * r (n - 1)
等差数列 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 );
上記のプログラムの変数aと変数dの部分をユーザに入力してもらうようにします。また、上記の場合は、等差数列の総和を求めていますが、 等比数列に変えて実行してみてください。クラス名は、Practice0805とします。
なお、等差数列も等比数列も第n項までの総和を求める公式があります。 等比数列は、次の金利計算のところで紹介します。等差数列は、以下のようになります。
total = n * ( 2 * a + ( n - 1 ) * d ) / 2 初項と公差から
total = n * ( a + z ) / 2 初項と末項から( z = an )
単利法…元金に期間数と利率を掛けたものが利息になりますそれでは、例えば定期預金で10,000円を3年預けた場合を考えてみましょう。年利率1%とします。3年後に額はいくらになるでしょうか?
複利法…期間毎に元金と利息の合計に利率が掛かかったものが次の利息になります
単利法…10000 + 10000 * 0.01 * 3 → 10,300円 複利法…(10000 * (1 + 0.01)) * (1 + 0.01 ) * (1 + 0.01) → 10,303円単利法と複利法では以上のように差がでてきます。利息の計算は通常は複利法で計算します。複利法の場合、貯める場合はより利息が大きくなります。逆にお金を借りる場合は、最初の利息が高くなります。また、数学的には複利法での計算は、等比数列の計算の応用となっています。
半年の利率 → 年率を2で割ればよい「金利」という言葉は通常は年率を示しています。注意しなければならないのは、消費者金融などで使われている実質年率と名目年率(約定年率)の違いです。
月率 → 年率を12で割ればよい(注1)
日部(ひぶ) → 年率365(366)で割ればよい
名目年率…預金金利や貸出金利のように契約で定められている年利率を指します。たとえば、名目年率が30%でも、その年の物価上昇率が2%であった場合には実質年率は28%になります。法律的には、年率は15%〜20%(金額による)を上限としています。消費者金融の実質年率が28%と表記されていましても、名目年率はそれよりもたぶん30%ですので実生活でも注意したいものです。ところで、法律的に決められた上限以上を支払う必要はありません。
実質年率…名目年率から物価上昇率(インフレ率)を差し引いた年利率です。
固定金利 年利率が一定
変動金利 年利率が相場に応じて変わる
上限つき変動金利 変動金利だが、上限(これ以上高くならない限度)はある
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 こんなに高い預金金利は不況下の日本では外国債ぐらいしかありません。でも、金融破綻もありましたし、外国債も為替変動の
影響を受けるのでリスクが高いことを承知していないといけません。。
貯める場合の年金…積立金(Deposit)
返済する場合の年金…賦金(ふきん:Installment)
期首払い…年金にも利息がつくたとえば、1万円の元金があって、年利率0.2%で毎年1000円の積立金であるような預金の場合に、期首払いでは1年後は11,022円ですが、期末払いでは11,020円となります。2円の差は、期首払いの場合に年金にも利息が付いたからです。賦金の場合は、期末払いがほとんどですが、その期に借りている額に対して利息が発生します。
期末払い…年金には利息が付かない
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) / 利率;
形式 FV (利率,期間数,定期支払額 {,現在価値}{,期末期首})
引数
・利率:一定期間の利率。
・期間数:支払い回数。
・定期支払額:期間ごとの支払額。
・現在価値:オプション。現在の投資額。
・期末期首:オプション。支払方法。0または1になります。各期間の期末に支払う場合は0を、期首に支払う場合は1を指定します。省略された場合は、0(期末払い)
例 =FV (0.09,12,-100,-3000) 結果=¥10452.07 (通貨書式を使った場合)この例では、¥3,000の投資の将来価値を示し、今後12年にわたり毎年¥100の投資を続ける場合、年率0.09(=9%)とすると、12年後にはこの投資の価値は¥10452.07になると、計算されています。
元金均等方式 … 月々決まった額の元金に利息を加えた金額を返済する方式
元利均等方式 … 借入元金と借入利息を合わせて月々、同じ金額を返済する方式
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; } } }
形式 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%という意味です。
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; // 総支払いに占める利息額
形式 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%という意味です。
整数の値として、適当な値を発生させることはシミュレーションなどではよく必要とされます。 そのような適当な値のことを乱数(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
上記のさいころの目を繰返しを用いて、30回発生させて見なさい。そして、「count1」から「count6」までの 変数を用いて、何回その目が発生したかをカウントするようなプログラムを作りなさい。 カウントされた6つの目の値を最後にターミナルかダイアログに出力しなさい。 Practice0810という名前でクラスを作ります。 30回を3000回、300000回と変えて見なさい。だんだん、発生する個数が一様になってきます。 このように乱数が発生する回数が高ければ高い程、分布が乱数本来の分布に近づくことを統計学では 「大数の法則」と呼んでいます。
モンテカルロ法(Monte Carlo method)は、最初の回で出てきたフォン・ノイマンが考えだした、 乱数を用いていろいろなシミュレーションを行なう手法のことを指しています。
最初に1回だけ、亀を横(右方向へ90度)に回します。 -5から5までの乱数を発生させ、それを亀を回す方向。亀を1ドット進めます。 乱数を発生させて、亀を進めるのを回数繰返し文で繰返して、亀がどこに向かうか見てみて下さい。 たとえば、100回・500回・800回・1000回などで繰返しの回数を変えて、実行してみましょう。 一様乱数で、大数の法則が効いてくれば、亀は水平(右方向へ90度)へ戻ってくる筈なのですが、、、、 結果は見てのお楽しみ。また、乱数を発生させる範囲を-3から3までや、-1から1までとして、 その違いについても考えて見なさい。 クラス名は、Practice0811です。
200回ぐらいの繰返しで、整数の乱数で前か左右に行くことを決めて、更に実数の乱数で進む距離 (例えば5〜15ぐらいの範囲)を決めるプログラムを書いて下さい。クラス名は、Pratice0712です。
ピタゴラスの定理を利用して、一定の円の中で動き回るタートルを作ってみて下さい。円の中心からの距離が、 半径を超えたら、適当な角度(例えば160〜200度の範囲内で乱数で決めて)で、反射するような形で、 タートルを動き回らせます。タートルのx座標の整数値は、getIntegerX( )で、タートルのy座標の整数値はgetIntegerY( )で求める ことができます。最初のx座標とy座標を記憶しておきます。動き回っている最中のx座標とy座標と最初の 座標の差分から距離を求めます。クラス名は、CreepWithinCircleです。
整数の乱数で、3〜8までの値を発生させ、値に応じて花びらが3枚から8枚までの花を描くような メソッドを定義して、そのメソッドを呼出しなさい。実際に、花が描けているかどうか、まず繰返しで 呼び出して確かめなさい。加えて、乱数で発生させて、横一列に様々な枚数の花びらを持つ花が出るように しなさい。 ちなみに、120度円弧を描き60度回転して120度円弧を描くような花びらを描く場合は、 花びらの枚数をm枚とすると、毎回花びらを描いた後、60-360/m度回転するとうまく描けるようです。 クラス名は、DrawRandomFlowerとします。
整数の乱数で、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 |