Ответ 1
Используйте srand() для установки семени. сохраните значение, которое вы использовали в качестве семени.
Я создаю несколько тысяч объектов в моей программе на основе функции С++ rand(). Хранение их в памяти было бы исчерпывающим. Есть ли способ скопировать семя CURRENT rand() в любой момент времени? Это даст мне возможность хранить ТОЛЬКО текущие семена, а не полные объекты. (таким образом, я мог бы регенерировать эти объекты, регенерируя точно такие же подпоследовательности случайных чисел)
Исчерпывающее решение - это хранение полной последовательности случайных чисел, заданных функцией rand() - не стоит. Другим решением будет реализация моего собственного класса для рандомизированных чисел.
Google не дал мне никаких положительных подсказок. Существуют сотни статей, посвященных основам rand и srand, и я не мог найти конкретных.
Кто-нибудь знает другие генераторы случайных чисел с внедренным мастером-перехватчиком?
Спасибо за ваши быстрые ответы! Есть более возможные ответы/решения по этому вопросу, поэтому я составил список ваших ответов здесь.
SOLUTIONS:
Короткий ответ: нет стандартного способа получить семя
Самое близкое возможное обходное решение - сохранить начальное семя в начале и подсчитать, сколько раз вы вызываете функцию rand(). Я отметил это как решение, потому что он работает с текущей функцией std:: rand() каждого компилятора (и это был главный вопрос). Я сравнил свой процессор с тактовой частотой 2,0 ГГц и обнаружил, что я могу вызвать & count rand() 1 000 000 000 раз за 35 секунд. Это может показаться хорошим, но у меня есть 80 000 вызовов для создания одного объекта. Это ограничивает количество поколений до 50 000, поскольку размер unsigned long. Во всяком случае, вот мой код:
class rand2
{
unsigned long n;
public:
rand2 () : n(0) {}
unsigned long rnd()
{
n++;
return rand();
}
// get number of rand() calls inside this object
unsigned long getno ()
{
return n;
}
// fast forward to a saved position called rec
void fast_forward (unsigned long rec)
{
while (n < rec) rnd();
}
};
Еще один способ - реализовать свой собственный генератор псевдослучайных чисел, как и предложенный Matteo Italia. Это самое быстрое и возможно лучшее решение. Вы не ограничены вызовами 4 294 967 295 rand() и не должны использовать другие библиотеки. Следует отметить, что разные компиляторы имеют разные генераторы. Я сравнил Matteo LCG с rand() в Mingw/GCC 3.4.2 и g++ 4.3.2. Все три из них были разными (с семенем = 0).
Используйте генераторы из С++ 11 или других библиотек, как предположили Cubbi, Jerry Coffin и Mike Seymour. Это лучшая идея, если вы уже работаете с ними. Ссылка для генераторов С++ 11: http://en.cppreference.com/w/cpp/numeric/random (здесь есть некоторые описания алгоритмов)
Используйте srand() для установки семени. сохраните значение, которое вы использовали в качестве семени.
Кто-нибудь знает другие генераторы случайных чисел с внедренным мастером-украдителем
Все стандартные генераторы случайных чисел С++ 11 (также доступные в TR1 и Boost) предлагают эту функцию. Вы можете просто скопировать объекты генератора или сериализовать/десериализовать их.
Нет стандартного способа получения текущего семени (вы можете установить его только через srand
), но вы можете переопределить rand()
(обычно это линейный конгруэнтный генератор) самостоятельно в нескольких строках кода:
class LCG
{
private:
unsigned long next = 1;
public:
LCG(unsigned long seed) : next(seed) {}
const unsigned long rand_max = 32767
int rand()
{
next = next * 1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
}
void reseed(unsigned long seed)
{
next = seed;
}
unsigned long getseed()
{
return next;
}
};
Классы генерации случайных чисел в С++ 11 поддерживают operator<<
, чтобы сохранить свое состояние (в основном семя) и operator>>
, чтобы прочитать его обратно. Таким образом, прежде чем создавать свои объекты, сохраните состояние, тогда, когда вам нужно сгенерировать одну и ту же последовательность, прочитайте состояние назад и отпустите.
rand()
не предлагает способ извлечь или дублировать семя. Лучшее, что вы можете сделать, это сохранить начальное значение семени, когда вы установите его с помощью srand()
, а затем восстановите всю последовательность из этого.
Функция Posix rand_r()
дает вам контроль над семенем.
Библиотека С++ 11 включает в себя библиотеку случайных чисел на основе "двигателей", генерирующих последовательность; эти двигатели могут быть скопированы и позволяют извлекать и восстанавливать их состояние с помощью операторов <<
и >>
, чтобы вы могли зафиксировать состояние последовательности в любое время. Очень похожие библиотеки доступны в TR1 и Boost, если вы еще не можете использовать С++ 11.
Вы могли бы попытаться сохранить значение, которое вы использовали для засева прямо перед (или после) srand.
Итак, например:
int seed = time(NULL);
srand(time(NULL));
cout << seed << endl;
cout << time(NULL);
Два значения должны быть одинаковыми.
Я бы рекомендовал вам использовать Mersenne Twister Pseudo-Random Number Generator. Он быстрый и предлагает очень хорошие случайные числа. Вы можете засеять генератор в конструкторе класса очень просто
unsigned long rSeed = 10;
MTRand myRandGen(rSeed);
Тогда вам просто нужно хранить где-то семена, которые вы использовали для генерации последовательностей...
Есть ли способ скопировать семя CURRENT из rand() в любой момент времени?
Далее следует способ реализации и восстановления состояния генератора псевдослучайных чисел (PRNG), который работает с библиотекой C на Ubuntu Linux (протестирован 14.04 и 16.04).
#include <array>
#include <cstdlib>
#include <iostream>
using namespace std;
constexpr size_t StateSize = 128;
using RandState = array<char, StateSize>;
void save(RandState& state) {
RandState tmpState;
char* oldState = initstate(1, tmpState.data(), StateSize);
copy(oldState, oldState + StateSize, state.data());
setstate(oldState);
}
void restore(RandState& state) {
setstate(state.data());
}
int main() {
cout << "srand(1)\n";
srand(1);
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << "srand(1)\n";
srand(1);
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << "save()\n";
RandState state;
save(state);
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << "restore()\n";
restore(state);
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
cout << " rand(): " << rand() << '\n';
}
Это зависит от:
rand()
и random()
, иЕсли запустить, это должно выводить:
srand(1)
rand(): 1804289383
rand(): 846930886
rand(): 1681692777
rand(): 1714636915
rand(): 1957747793
rand(): 424238335
rand(): 719885386
rand(): 1649760492
srand(1)
rand(): 1804289383
rand(): 846930886
rand(): 1681692777
rand(): 1714636915
save()
rand(): 1957747793
rand(): 424238335
rand(): 719885386
rand(): 1649760492
restore()
rand(): 1957747793
rand(): 424238335
rand(): 719885386
rand(): 1649760492
Это решение может помочь в некоторых случаях (код, который нельзя изменить, воспроизвести выполнение для цели отладки и т.д.), но он явно не рекомендуется в качестве общего (например, использовать С++ 11 PRNG, который правильно поддерживайте это).