(no subject)
Feb. 9th, 2008 12:14 pm* выловив сложный баг в чужой Монте-Карло-симуляции полимеров.
Товарищи программисты! Пожалуйста, никогда не делайте равномерно распределённые числа от 0 до (N - 1) вот так:
И вот так пожалуйста тоже никогда не делайте:
В первом случае вы получаете неравномерное распределение. Во втором вы никогда не получите бОльшей части возможных чисел. В первом случае, я надеюсь, всё ясно: если (MAX_RAND + 1) на N не делится, распределение будет неравномерным — первые (MAX_RAND % N) чисел будут иметь бОльшую вероятность выпадения. И когда ваш N по числой случайности оказался в районе 2/3 MAX_RAND это означает, что числа с 0 до N/2 будут выпадать вдвое чаще, чем числа с N/2 до N, а это не шутки — это кривая и нихера не рабоающая симуляция.
Со вторым случаем тоже всё понятно. rand_01() выдаёт вам равномерно распределённый от 0 (включительно) до 1 (невключительно) double. Мантисса double 52 бита, а длина ulong в диалекте, обычно используемом на Sun'овских Grid'ах — 64 бита. Т.е. при большом N будет и незаполнение всей области значений ulong'а и весьма поганые ошибки окрулегния.
Что делать? Если у вас программный генератор случайных чисел, универсальных решений нет. Но в хороших софтовых генераторах есть функция rand(N) которая делает то, что вам нужно оптимальным образом. Если вам дана такая благодать, как железный генератор случайных чисел, заполняющий весь ulong, возрадуйтесь и сделайте что-то такое:
Товарищи программисты! Пожалуйста, никогда не делайте равномерно распределённые числа от 0 до (N - 1) вот так:
r = rand_ulong() % N
И вот так пожалуйста тоже никогда не делайте:
r = (ulong) (rand_01() * N)
В первом случае вы получаете неравномерное распределение. Во втором вы никогда не получите бОльшей части возможных чисел. В первом случае, я надеюсь, всё ясно: если (MAX_RAND + 1) на N не делится, распределение будет неравномерным — первые (MAX_RAND % N) чисел будут иметь бОльшую вероятность выпадения. И когда ваш N по числой случайности оказался в районе 2/3 MAX_RAND это означает, что числа с 0 до N/2 будут выпадать вдвое чаще, чем числа с N/2 до N, а это не шутки — это кривая и нихера не рабоающая симуляция.
Со вторым случаем тоже всё понятно. rand_01() выдаёт вам равномерно распределённый от 0 (включительно) до 1 (невключительно) double. Мантисса double 52 бита, а длина ulong в диалекте, обычно используемом на Sun'овских Grid'ах — 64 бита. Т.е. при большом N будет и незаполнение всей области значений ulong'а и весьма поганые ошибки окрулегния.
Что делать? Если у вас программный генератор случайных чисел, универсальных решений нет. Но в хороших софтовых генераторах есть функция rand(N) которая делает то, что вам нужно оптимальным образом. Если вам дана такая благодать, как железный генератор случайных чисел, заполняющий весь ulong, возрадуйтесь и сделайте что-то такое:
ulong rand(ulong n) {
ulong r, truncate = (MAX_ULONG % N) + 1;
do {
r = rand_ulong();
} while(r < truncate )
return (r - truncate) % n;
}