Pull to refresh

Comments 33

А какой define ставить если нужен детерминированный генератор?
О, средства для статмоделирования мы любим! На первый взгляд библиотека выглядит очень хорошо, код написан понятно и качественно, приятно читать. Будем тестировать!
1
«RandLib избавляет пользователя от двух вещей: выбора базового генератора (функции, возвращающей целое число от 0 до некого RAND_MAX) и выбора стартовой позиции для случайной последовательности (функция srand()). Сделано это во имя удобства, так как многим пользователям этот выбор скорее всего ни к чему.»

— Не согласен.Для отладки очень часто нужна именно повторяемость.

2
Могут быть проблемы со статическими переменными в функциях BasicRandGenerator<..>::Variate() в многопоточной среде, лучше сделать их членами класса.

>Не согласен.Для отладки очень часто нужна именно повторяемость.
Поддерживаю, фактически это сильно ограничивает использование библиотеки во всяких научных вычислениях
Автору
Вообще — типичный код для математика: отличная алгоритмическая работа и значительно более слабая (хотя и неплохая) реализация. Фунцию Variate поправьте обязательно, однозначно будет проблема со скоростью (конфликт read-modify-write на общей памяти)
Кроме того, статические переменные затрудняют работу оптимизатора если Вы используете более одного экземпляра класса
1. Согласен с тем, что нужно добавить функционал на подобие srand. Но не стоит заставлять пользователя всегда его использовать. В целом, Вы правы.

2. Наверное, еще лучше их сделать static thread_local. Однако это сильно повлияет на скорость. В таком случае надо предоставлять выбор. В конце концов, rand() сделан по схожему принципу и тоже не является потоко-безопасным.
rand() — отвратная и древняя функция,static thread_local — не нужно, нужны именно переменные члены класса или структуры. Еще раз, статические переменные затрудняют работу оптимизатора если Вы используете более одного экземпляра класса даже в одном потоке.
Возьмите код отсюда
* 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 и это лучший генератор который я видел
лучший Mersenne Twister генератор который я видел :). А вообще спасибо, посмотрю обязательно. Мне скорость и качество очень критичны(RANSAC like code).
Да это прикольная штука, правда мне не доводилось сталкиваться с задачами в которых нужен бы что-то большее чем rand. Но вот те кто пишут казино им там законы распределения вероятности явно пригодятся.
Казино на бэкэнде маловероятно используют С++.
Скорость разработки при большей отказоустойчивости в данном случае выше необходимости иметь оптимальное потребление памяти и бОльшую скорость вычислений. Кроме того легальные букмекерки использую источники событий данных с околоистинными рандомами физического мира, которые были некогда замерены, дабы владельцы не имели возможности смухлевать или подкрутить рандомы в свою пользу. Так что для казино данная конкретная либа врядли пригодится.

Я не специалист в матстатистике, но если бы мне понадобилась подобная библиотека для чего-то серьёзного, то первым делом я бы поинтересовался качеством полученных распределений. Они какие-нибудь тесты на случайность проходят? Всякие NIST и Die Hard там.


И ещё: в C++17 точно никак нельзя было обойтись без макросов? Вы бы их хотя бы префиксом RANDLIB_ снабдили, а всё остальное запихнули в неймспейс.

По умолчанию, основной генератор — это JKISS из статьи Good Practice in (Pseudo) Random Number Generation for Bioinformatics Applications. Он проходит все DieHard тесты. Генераторы для прочих распределений используют именно его.

Грешен, но упомянутые макросы единственные на всю библиотеку. Знаю, какая на них реакция в целом, но в данном случае их использование было удобно мне как разработчику, и виделось удобным пользователю. Про префикс Вы правы, будет учтено.

Ещё вопрос возник: если я вдруг захочу генерировать числа точности выше двойной (например, такие, как в boost::multiprecision), ваша библиотека справится? А то я посмотрел исходники класса NormalRand, а там везде double захардкожен. И если такая возможность есть, просто я проглядел, не пострадают ли при этом статистические характеристики распределений?

Пока что это не поддерживается. Но предложение хорошее!
Лучше попробуйте мою либу, C++11, header-only, всё на шаблончиках, никаких макросов. Поддерживает многопоточность, и какие угодно распределения через шаблонные параметры, и конечто же много юнит тестов. Делает работу с рандомом проще в 100 раз.

Узнать больше:
github.com/effolkronium/random
Ваша библиотека выглядит прелестно. Но это лишь обертка вокруг функций std, а RandLib в свою очередь полная альтернатива стандартной библиотеке. Кроме того, у Вас только генераторы. Возможности RandLib куда шире (функции распределения, моменты и т.п.). Тут не уложиться в header-only.
Обёртка не только вокруг std, в шаблонные параметры можно закинуть любые генераторы и распределители
А причём тут с++17?
Макросы, непойми какая поддержка многопоточности. Оно вобще не упадёт если в несколько потоков генерировать случайные последовательности?
Про поддержку многопоточности в статье указано не было, но в дальнейших версиях это будет учтено. Смысл Вашего комментария лишь в том чтобы повторить вышесказанное.
По поводу С++17, он в основном был нужен ввиду появившихся в нем специальных математических функций.
#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
Если мы работаем в одном потоке, нужно ли нам создавать несколько экземпляров FRand? Я исходил из того, что нет, и поэтому генератор был статическим. Иначе, для разных экземпляров может возникать проблема коллизии. Или я не прав?
Вот именно для отсутствия коллизий и нужно Ваши статические переменные переложить в members, как в моем примере. Компилятор использует больше регистров и уменьшает latency. посмотрите разницу между 2 и 3 примером по скорости
Возможно, у нас путаница в терминологии. Под коллизией я подразумевал совпадения значений seed у двух экземпляров. В Вашем примере они и так равны по умолчанию. Но две одинаковые выборки нам не нужны.
А если у меня не два, а несколько десятков экземпляров FRand? Мне для каждого seed запоминать?

P.s. Если использовать только time, то seedы тоже будут одинаковыми, если созданы в одно время.
Больше 2 в одном потоке выигрыша по скорости скорее всего не дадут.
А зачем Вам 10? Первый рандом инит из time остальные из первого:
FRand fr(time_count());
FRand fr1(fr.rand());
FRand fr2(fr.rand());
Only those users with full accounts are able to leave comments. Log in, please.