Как получить текущий seed из C++ rand()?
Я генерирую несколько тысяч объектов в своей программе на основе функции C++ rand(). Хранение их в памяти было бы исчерпывающим. Есть ли способ скопировать текущее семя rand() в любой момент времени? Это дало бы мне возможность хранить только текущие семена, а не полные объекты. (таким образом, я мог бы регенерировать эти объекты, регенерируя точно такие же под-последовательности случайных чисел)
Исчерпывающим решением является хранение полной последовательности случайных чисел, заданных by rand () - не стоит. Другимрешением было бы реализовать мой собственный класс для рандомизированных чисел.
Google не дал мне никаких положительных подсказок. Есть сотни статей, обучающих основам Рэнд и сранд, и я не смог найти конкретные из них.Кто-нибудь знает другие генераторы случайных чисел с реализованным похитителем семян?
Спасибо за быстрые ответы! Существует больше возможных ответов/решений на этот вопрос, поэтому я сделал список ваших ответов здесь.
Решения:
-
Короткий ответ: нет стандартного способа получить семя
-
Самое близкое решение-сохранить начальное семя в начале и подсчитать, сколько раз вы вызываете функцию rand (). Я отметил это как решение, потому что оно работает на текущей std::rand() функции каждого компилятора (и это был главный вопрос). Я провел сравнительный анализ моего процессора 2,0 ГГц и обнаружил, что я может вызывать и считать 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(); } };
-
Другой способ-реализовать свой собственный генератор псевдослучайных чисел, как предложил Маттео Италия. Это самое быстрое и, возможно, лучшее решение. Вы не ограничены 4,294,967,295 rand () вызовы, и не нужно использовать другие библиотеки также. Стоит отметить, что разные компиляторы имеют разные генераторы. Я сравнил LCG Маттео с rand() в Mingw/GCC 3.4.2 и G++ 4.3.2. Все 3 из них были разными (с семенем = 0).
-
Используйте генераторы из C++11 или других библиотек, как предлагали Cubbi, Джерри Коффин и Майк Сеймур. Это лучшая идея, если вы уже работаете с ними. Ссылка для генераторов C++11: http://en.cppreference.com/w/cpp/numeric/random (здесь также есть некоторые описания алгоритмов)
8 ответов:
Используйте функцию srand () для задания начального значения. сохраните значение, которое вы использовали в качестве семени.
Кто-нибудь знает другие генераторы случайных чисел с реализованным похитителем семян
Все стандартные генераторы случайных чисел C++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; } };
Классы генерации случайных чисел в C++11 поддерживают
operator<<
для хранения своего состояния (в основном начального) иoperator>>
для его чтения обратно. Итак, в основном, прежде чем создавать объекты, сохраните состояние, а затем, когда вам нужно повторно сгенерировать ту же последовательность, считайте состояние обратно, и вперед.
rand()
не предлагает никакого способа извлечь или продублировать семя. Лучшее, что вы можете сделать, это сохранить начальное значение семени, когда вы устанавливаете его с помощьюsrand()
, а затем восстановить всю последовательность из этого.Функция Posix
Библиотека C++11 включает библиотеку случайных чисел, основанную на генерирующих последовательность "движках"; эти движки копируемы и позволяют извлекать и восстанавливать их состояние с помощью операторовrand_r()
дает вам контроль над семенем.<<
и>>
, так что вы можете захватить состояние последовательности в любое время. Очень похожие библиотеки доступны в TR1 и Boost, если вы еще не можете использовать C++11.
Вы можете попробовать сохранить значение, которое вы использовали для посева непосредственно перед (или после) srand.
Так, например:
int seed = time(NULL); srand(time(NULL)); cout << seed << endl; cout << time(NULL);
Эти два значения должны быть одинаковыми.
Я бы рекомендовал вам использовать генератор псевдослучайных чисел Мерсенна твистера. Он быстр и предлагает очень хорошие случайные числа. Вы можете очень просто затравить генератор в конструкторе класса с помощью
Тогда вам просто нужно где-то хранить семена, которые вы использовали для генерации последовательностей...unsigned long rSeed = 10; MTRand myRandGen(rSeed);
Есть ли способ скопировать текущее семя 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'; }
Это зависит от:
- один и тот же PRNG используется библиотекой C для предоставления интерфейсов
rand()
иrandom()
, а также- некоторые знания о инициализации по умолчанию это PRNG в библиотеке C (Состояние 128 байт).
При запуске это должно вывести:
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
Это решение может помочь в некоторых случаях (код, который нельзя изменить, воспроизведение выполнения для отладки и т. д...), но он явно не рекомендуется в качестве общего (например, использовать C++11 PRNG, который должным образом поддерживает это).