C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト


(過去ログ 18 を表示中)
■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

となったのですが、どっか変でしょうか?

返信 編集キー/


管理者用

- Child Tree -