| ■7110 / ) |
Re[16]: Long型の乱数を生成する自作クラスをつくりたい |
□投稿者/ れい (88回)-(2007/08/30(Thu) 12:42:05)
|
■No7096 (ぼのぼの さん) に返信 > ■No7076 (れい さん) に返信 > >>ul = umin + ul Mod (umax - umin + 1UL) >> >>こうやって余りを計算してしまうと、 >どうしても偏りが出てしまいます。 > > ということは、乱数が折り返しのときだけ > サイコロを振りなおすようにする、とすると… >
考え方は正解です。
どんなに大きなビット数の乱数でも、どんなに精度がよい乱数でも、 乱数の範囲を変えるときはかならず同じ問題に遭遇します。 普通乱数の実装は2進数の整数で組まれていますから、 任意の範囲に対して「振り直し無し」の方法は理論上ありません。
忘れがちですが、DoubleからInt型を作り出すときも同様で、 情報が足りなくならないよう気をつける必要があります。
せっかくMT使ってるのに、範囲変更でmoduloとっただけの実装をたまにみますが、 猫に小判ですね。 乱数を使うような場合、かなりの確率で統計的なデータ処理を行いますが、 このミスは統計的に効いてきます。
> こうかな。う〜んあってるのかな?
コードは…変です。
> ループが最も多く発生するのはumax-uminがUInt64.MaxValueの半分より > ちょびっとだけ大きい値の場合だけど、 > NextBytesの一様性がある程度のものならこの場合でもNextBytesを呼ぶ回数は約2倍。 > と期待されるので、そんなに激しい性能劣化は無いと思われまする。
乱数が範囲内にある確率をAとすると、乱数を生成する回数の期待値Sは S = A + (1-A)*(S+1) = 1/A なので、ワーストケースでの期待値は約2回です。
NextBytesは1バイト毎にサイコロ振ってますので、 トータルで期待値として16回サイコロを振ってることになります。
> もっといい方法があるのでしょうか…
コード的にはいろいろあるでしょうが、 考え方はこれが最良です。
|
|