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