Rndについて考える

Rndは必要か?

ボタンを押すたびに「でたらめな数」を表示するプログラムは、Rndという関数を使っている。ただでたらめな数を作るだけなのに、どうしてRndが必要なのだろう。Rndを使わないと絶対にでたらめな数は作れないだろうか?

でたらめな数を作る実験その1

 

たらめな数値を作り、でたらめな計算をすれば、結果の数値がどうなるかは予想できない。これを利用してでたらめな数値を作ってみよう。

a = 0.392894920295
b = 0.495204020345
x = a * a - b * b - a * b - a * a * b
y = a - b * b * b - a * b * (a - 1) - a * (b + 1) * b
z = Math.Sin(a) - Math.Sin(b) - a ^ b
Label1.Text = "x=" & x & " ,y=" & y & " ,z=" & z

一回目に表示される数値は一見でたらめに見える。しかし、繰り返しボタンをクリックすると、結果は毎回まったく同じである。

結論: めちゃくちゃな計算をすると、でたらめな数値が得られるが、何回繰り返しても毎回同じ値になる。

 

 

でたらめな計算その2

以前計算誤差をというものがあることを学んだ。たとえば以下の計算をすると、計算誤差によりxは0にならない。

x = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
x = x - 1

計算誤差は、誤差なのだから、規則性のないでたらめな値にならないだろうか。そこでこんなプログラムを作ってみる。

x = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
x = x - 1
y = x * 10000
z = x * 12135
Label1.Text = "x=" & x & " ,y=" & y & " ,z=" & z

計算誤差の部分を拡大することで、でたらめな数を作ろうという作戦だ。しかし、実際に実行すると、2回目以後も1回目とまったく同じ値になる。

結果: 計算誤差はでたらめな数値であるが、同じ計算をした時の計算誤差は何回繰り返しても完全に同じである。

 

このように試してみると、でたらめには2種類ある。

(1) 実行してみるまで予想できない数値

(2) 実行する毎に、毎回異なった数値

でたらめな計算や計算誤差は(1)の意味ではでたらめであるが、(2)の性質がない。計算機の機能のほとんどは、毎回厳密に同じ結果が得られるように作られている。そのため、毎回異なったでたらめな結果を得るためには、Rndという特殊な関数が必要になる。
このように、通常はプログラムの実行結果は、 「異なった入力を与えない限り100%同じ結果になる」ということが特長だ。 これはコンピュータに慣れていない人の直感とは異なるかもしれないので、 よく覚えておこう。

Rnd(1) が 1.0にならないのはなぜか?

Rnd(1)が1.0を含まないのはなぜ?
Rnd(1)の範囲は 0.0以上1.0未満になっている。どうしてRnd(1)の値は1.0「未満」なのだろうか

Rnd(1)の値の範囲を3分割すると、3つの区間はたとえば以下のようになる。

区間1 区間2 区間3
0 ≦ x < 1/3 1/3 ≦ x < 2/3 2/3 ≦ x < 1


もしも、Rnd(1)の値に1.0が含まれると、3番目の区間に1を含めなければならない。

区間1 区間2 区間3
0 ≦ x < 1/3 1/3 ≦ x < 2/3 2/3 ≦ x ≦ 1

非常に些細なことだが、区間3だけ不等号が異なってしまう。

 

1箇所だけ異なる不等式を書くのは気持ちが悪い。また、うっかり以下のようなプログラムを書いてしまうかもしれない。

プログラム例1

    If r>=0 And r< 1/3 Then
        x = 1
    Else If r>=1/3 And r<2/3 Then
        x = 2
    Else If r>=2/3 And r<1 Then
        x = 3
    End If               
   

このプログラムは rが 1の場合はnに代入されないので、1/1000000の率で誤作動を起こす。Rndが1.0を含まなければそのような問題はおきないし、以下のようなたいへん簡単な数式で同じ動作をさせることができる。

    x = Int(Rnd(1) * 3) + 1

 

もしRndが1になる場合もあるとすると、その場合だけ x が4になってしまう。それを避けるには以下のようなプログラムにしなければならない。これはとてもみっともない。

    x = Int(Rnd(1) * 3) + 1
    If x = 4 Then
    x = 3
    End If

 

最後に、1.0を含むことでどの程度影響があるかを考えてみる。1.0の分だけ区間3が区間1, 区間2より高い確率で発生する。仮に0.000001刻みででたらめな数値が作られるとすると、1が出るのは1000000回に一回なので、1/1000000程度の偏りが生じる。この動作確認を慎重に行わないと、この程度の偏りがあることを検出するのはむずかしい。

しかし、これが気象シミュレーションや人工衛星の軌道計算の一部であれば、本来均等であるべきものが1/1000000の偏りがあることは致命的な被害につながる可能性がある。

まとめ: Rnd(1)の範囲は、0以上1「未満」が合理的で便利だ。Rndのプログラムの間違いは気づきにくいので、ソースコード上で何度も注意深く確認する必要がある。