Comments 33
«RandLib избавляет пользователя от двух вещей: выбора базового генератора (функции, возвращающей целое число от 0 до некого RAND_MAX) и выбора стартовой позиции для случайной последовательности (функция srand()). Сделано это во имя удобства, так как многим пользователям этот выбор скорее всего ни к чему.»
— Не согласен.Для отладки очень часто нужна именно повторяемость.
2
Могут быть проблемы со статическими переменными в функциях BasicRandGenerator<..>::Variate() в многопоточной среде, лучше сделать их членами класса.
Поддерживаю, фактически это сильно ограничивает использование библиотеки во всяких научных вычислениях
Вообще — типичный код для математика: отличная алгоритмическая работа и значительно более слабая (хотя и неплохая) реализация. Фунцию Variate поправьте обязательно, однозначно будет проблема со скоростью (конфликт read-modify-write на общей памяти)
Кроме того, статические переменные затрудняют работу оптимизатора если Вы используете более одного экземпляра класса
2. Наверное, еще лучше их сделать static thread_local. Однако это сильно повлияет на скорость. В таком случае надо предоставлять выбор. В конце концов, rand() сделан по схожему принципу и тоже не является потоко-безопасным.
* brief Tiny Mersenne Twister only 127 bit internal state
*
* author Mutsuo Saito (Hiroshima University)
* author Makoto Matsumoto (University of Tokyo)
github.com/MersenneTwister-Lab/TinyMT
На моей машине дает (3.2 GHz ) 2.7 ns per uniform number и это лучший генератор который я видел
1.1 ns (rnd32) бит на том же тесте и моей платформе: habrahabr.ru/post/323158/#comment_10503460
Я не специалист в матстатистике, но если бы мне понадобилась подобная библиотека для чего-то серьёзного, то первым делом я бы поинтересовался качеством полученных распределений. Они какие-нибудь тесты на случайность проходят? Всякие NIST и Die Hard там.
И ещё: в C++17 точно никак нельзя было обойтись без макросов? Вы бы их хотя бы префиксом RANDLIB_
снабдили, а всё остальное запихнули в неймспейс.
Грешен, но упомянутые макросы единственные на всю библиотеку. Знаю, какая на них реакция в целом, но в данном случае их использование было удобно мне как разработчику, и виделось удобным пользователю. Про префикс Вы правы, будет учтено.
Ещё вопрос возник: если я вдруг захочу генерировать числа точности выше двойной (например, такие, как в boost::multiprecision
), ваша библиотека справится? А то я посмотрел исходники класса NormalRand
, а там везде double
захардкожен. И если такая возможность есть, просто я проглядел, не пострадают ли при этом статистические характеристики распределений?
Узнать больше:
github.com/effolkronium/random
Макросы, непойми какая поддержка многопоточности. Оно вобще не упадёт если в несколько потоков генерировать случайные последовательности?
#define UNIDBLRAND // генератор дает большее разрешение за счет комбинации двух случайных чисел
#define JLKISS64RAND // генератор дает большее разрешение за счет генерации 64-битных целых чисел
#define UNICLOSEDRAND // U может вернуть 0 или 1
#define UNIHALFCLOSEDRAND
А если мне нужно два числа подряд, одно обычное, а другое «повышенного» разрешения? Да и вообще, зачем здесь definы? Не лучше ли было бы этот выбор делать через шаблонный параметр, аргумент функции или просто через имя функции? Также было бы интересно увидеть сравнение производительности с другими аналогичными библиотеками, помимо stl. Вот, например: www.pcg-random.org
const int Cycles = 100000000;
FRand fr;
FRand fr1;
{
HiCl cl("gen");
summ=0;
for (int k=0;k<Cycles;k++)
{
summ+=fr.urand();
}
}
cout<<summ/Cycles<<"\n";;
{
HiCl cl("gen");
summ = 0;
summ1 = 0;
for (int k=0;k<Cycles/2;k++)
{
summ+=fr.urand();
summ1+=fr.urand();
}
}
cout<<(summ+summ1)/Cycles<<"\n";
{
HiCl cl("gen");
summ=0;
summ1 = 0;
for (int k=0;k<Cycles/2;k++)
{
summ+=fr.urand();
summ1+=fr1.urand();
}
}
cout<<(summ+summ1)/Cycles<<"\n";
//log
gen 351.955 ms
0.499969
gen 347.921 ms
0.500026
gen 272.77 ms
0.499971
Собственно оболочка:
#ifndef FRand_H
#define FRand_H
extern "C"
{
#include <tinymt32.h>
}
class FRand
{
tinymt32_t tinymt;
public:
FRand()
{
tinymt.mat1 = 0x8f7011ee;
tinymt.mat2 = 0xfc78ff1f;
tinymt.tmat = 0x3793fdff;
int seed = 1;
tinymt32_init(&tinymt, seed);
}
FRand(int seed)
{
tinymt.mat1 = 0x8f7011ee;
tinymt.mat2 = 0xfc78ff1f;
tinymt.tmat = 0x3793fdff;
tinymt32_init(&tinymt, seed);
}
inline double urand()
{
return tinymt32_generate_32double(&tinymt);
}
uint32_t rand()
{
return tinymt32_generate_uint32(&tinymt);
}
};
#endif
Инициализируйте разными seed из того же time
P.s. Если использовать только time, то seedы тоже будут одинаковыми, если созданы в одно время.
RandLib. Библиотека вероятностных распределений на C++17