Потребовалась функция ГСЧ с гнерацией числа Int от 0 до любого значения.
Получилась такая функция. Думаю распределение получилось равномерным.
int RandomInteger(int max_vl){return (int)MathFloor((MathRand()+MathRand()*32767.0)/1073741824.0*max_vl);}//случайное Int от 0 до 1073741824
На форуме посоветовали другую функцию из статьи. Отбросив лишнее,
получилось:
//если из define переместить в код RNDUint, то скорость работы увеличится на 30% для 10 млн повторов с 600 мс до 850 мс #define xor32 xx=xx^(xx<<13);xx=xx^(xx>>17);xx=xx^(xx<<5) #define xor128 t=(x^(x<<11));x=y;y=z;z=w;w=(w^(w>>19))^(t^(t>>8)) #define inidat x=123456789;y=362436069;z=521288629;w=88675123;xx=2463534242 class RNDUint{ protected: uint x,y,z,w,xx,t; public: RNDUint(void){inidat;}; ~RNDUint(void){}; uint Rand() {xor128;return(w);};//равномерное распределение на отрезке [0,UINT_MAX=4294967295]. double Rand_01() {xor128;return((double)w/UINT_MAX);};//равномерное распределение на отрезке [0,1]. void Reset() {inidat;};//сброс всех исходных значений в первоначальное состояние. void SRand(uint seed) {//установка новых исходных значений генератора.seed= [0,UINT_MAX=4294967295]. При seed=0 функция меняет начальные значения случайным образом. int i;if(seed!=0){xx=seed;}for(i=0;i<16;i++){xor32;}xor32;x=xx;xor32;y=xx;xor32;z=xx;xor32;w=xx;for(i=0;i<16;i++){xor128;} }; };
Сделал сравнение по скорости обоих функций, оригинальной из статьи и просто MathRand():
Оригинальная из статьи Rnd.Rand_01() - 602 мс
Укороченная по статье rnu.Rand_01() - 596 мс
Моя первоначальная
версия RandomInteger() - 840 мс
Просто по MathRand() - 353 мс (распределение будет равномерным до 32767, если диапазон требуемых
чисел больше, то результат будет с пропусками.)
Для оценки распределения вывожу все 1000000 точек на экран в виде квадрата
1000*1000. Чем больше попаданий ГСЧ в точку, тем ярче ее цвет.
Задал 10 млн повторений ГСЧ, в
среднем в каждую точку должно попасть 10 раз.
Распределение вероятности для rnu.Rand_01() от 0 до 1 000 000.
Как видим первые 2 имеют достаточно равномерное распределение, а по MathRand() имеет пропуски.
Пропуски обусловлены разрешением MathRand = 32767. При множителе 1000000 получим пропуски 1000000/32767=30 точек. MathRandomUniform
по картинке похожа, видимо те же пропуски в 30 единиц.
Другой вариант, зададим максимальное число 30000. Первые 2 равномерны, как при
миллионе. MathRand выглядит так (с увеличеным куском):
Пропусков (черных точек до позиции 30000) нет. Но часть заметно ярче. Это неравномерность округления 30000/32767. Каждая 11-я точка получает вдвое больше попаданий.
Что-то равномерное можно получить от MathRand при максимуме до 3000... 4000. Вот увеличенный вариант для 3500:
Первые два варианта думаю при приближении максимального числа к 100 миллионам для моей функции (у которой разрешение
около 1 миллиарда) и 400 млн. при разрешении 4 млрд. для RND. - тоже начнут неравномерно распределяться за счет округления.
Доработаем:
long RandomLong(long max_vl){return (long)MathFloor((MathRand()+MathRand()*32767.0+ MathRand()*1073741824.0)/35184372088832.0*max_vl);}//случайное long от 0 до 35184372088832
Точность 35184 миллиардов, т.е. при максимальной точке до 3000 миллиардов распределение будет равномерно. Функция стала на 30% медленнее RandomInteger, за счет третьего вызова MathRand().
Для себя решил пользоваться своей функцией, она медленнее на 25% чем RND, но компактнее и понятнее.
Примечание:
Разработчик RND из статьи указал, что
При каждом перезапуске, ставлю seed=0, но картинка с распределениями при перезапусках не меняется. Т.е. утверждение неверное. Т.о. надо для случайной инициализации надо seed= устанавливать в случайное число, например seed=GetTickCount();
Для моей функции RandomInteger() при перезапусках видно перераспределение точек. Если установить srand(0); то при перезапусках распределение тоже начинает повторяться. Т.е. для этой функции для случайной инициализации надо либо не вызывать srand, либо тоже с меткой врмени MathSrand(GetTickCount()).
Файл приложил, можете повторить эксперимент.