| ■7120 / ) |
Re[14]: Long型の乱数を生成する自作クラスをつくりたい |
□投稿者/ ぼのぼの (82回)-(2007/08/30(Thu) 13:40:09)
|
■No7112 (れい さん) に返信
> コピペなどで、作者の元を離れ、容易に伝播していきそうなコードです。
> そう考えるなら、
> きちんと一様性のあるアルゴリズムで実装しておくか、
> 一様性の無いことをきちんと認識し、コードに書いておかなくては
> 大変なことになりませんか?
そんなの関係ねぇ!と言ってしまうのは簡単ですが。
■No7110 (れい さん) に返信
> 考え方は正解です。
>
> (中略)
>
> コードは…変です。
やっぱり気になりますね(^^;
簡単に考えるために、仮にUInt64.MaxValueを99と仮定します。
となると、生成される乱数は0〜99の100通りあることになります。
で、例えばuminが0、umaxが9の場合はumax-umin+1=10できっちり分割されるので、
サイコロを振りなおす必要はないわけです。
この「きっちり分割されるケースかどうか」の判定に関しては、
100÷10の余りが0かどうか、で判定すれば良いのですが、
MaxValueである99に1を足すとオーバーフローが発生してしまうので、
99÷10の余りが9かどうか、という判定方法を使っています。
これをコード化すると、
Dim divVal As UInt64 = umax - umin + 1UL
If UInt64.MaxValue Mod (divVal) = (umax - umin) Then 振りなおす必要なし
となります。
次に、例えばuminが0、umaxが10の場合、umax-umin+1=11なので、
余 | 0 1 2 3 4 5 6 7 8 9 10
----+----------------------------------
商 0| 0 1 2 3 4 5 6 7 8 9 10
1| 11 12 13 14 15 16 17 18 19 20 21
2| 22 23 24 25 26 27 28 29 30 31 32
:
8| 88 89 90 91 92 93 94 95 96 97 98
9| 99
余りが0のケースだけ1回多い。つまり99のときだけ
サイコロが振りなおされれば良いことになります。
これを拡げて考えると、「最下行のときは振りなおす」となり、
この「最下行かどうか」の判定は「商がMaxValueを割った商と同じか」でできるので、
Dim divVal As UInt64 = umax - umin + 1UL
If ul / divVal = UInt64.MaxValue / divVal Then 振りなおし!
というコードが出てきます。
ただし、VBだと/演算子は小数の結果を返すので、実際には比較前に整数化しないといけません。
#そういえば、\演算子なんてのがあったことに今気がついた
つまり、「きっちり分割できないパターンでかつ最下行なら振りなおせ」となり、
その結果、
Dim divVal As UInt64 = umax - umin + 1UL
While UInt64.MaxValue Mod (divVal) <> (umax - umin) _
AndAlso Convert.ToUInt64(ul / divVal) = Convert.ToUInt64(UInt64.MaxValue / divVal)
_Random.NextBytes(b)
ul = BitConverter.ToUInt64(b, 0)
End While
となったのですが、どっか変でしょうか?
|
|