■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 -