Есть ли альтернатива использованию времени для генерации генерации случайных чисел?
Я пытаюсь запустить несколько экземпляров кода (2000 экземпляров или около того) одновременно в вычислительном кластере. Способ его работы заключается в том, что я отправляю задания, и кластер будет запускать их, поскольку узлы открываются так часто, с несколькими заданиями на node. Это, похоже, дает одинаковые значения для большого числа экземпляров в их генерации случайных чисел, в которых используется время-семя.
Есть ли простая альтернатива, которую я могу использовать вместо этого? Воспроизводимость и безопасность не важны, быстрое генерирование уникальных семян. Какой был бы самый простой подход к этому, и, если возможно, кросс-платформенный подход был бы хорошим.
Ответы
Ответ 1
Инструкция rdtsc
- довольно надежное (и случайное) семя.
В Windows он доступен через встроенный __rdtsc()
.
В GNU C он доступен через:
unsigned long long rdtsc(){
unsigned int lo,hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return ((unsigned long long)hi << 32) | lo;
}
Команда измеряет полные псевдоциклы с момента включения процессора. Учитывая высокую частоту сегодняшних машин, крайне маловероятно, что два процессора возвратят одно и то же значение, даже если они будут загружаться одновременно и будут синхронизированы с одинаковой скоростью.
Ответ 2
Я предполагаю, что у вас есть процесс запуска других процессов. Передайте это в семя, чтобы использовать. Тогда вы можете заставить этот мастер-процесс просто пройти случайное число для каждого процесса, который будет использоваться в качестве его семени. Таким образом, на самом деле выбрано только одно произвольное семя... вы можете использовать для этого время.
Если у вас нет мастер-процесса, запускающего другие, то, если у каждого процесса есть хотя бы уникальный индекс, то вы можете сделать один процесс, который генерирует ряд случайных чисел в памяти (если используется совместно используемая память) или в файле (если общий диск), а затем каждый процесс вытаскивает случайный номер index'а для использования в качестве своего семени.
Ничто не даст вам более равномерного распределения семян, чем ряд случайных чисел из одного семени.
Ответ 3
Комбинация PID и времени должно быть достаточным для получения уникального семени. Это не 100% кросс-платформенный, но getpid(3)
на платформах * nix и GetProcessId
в Windows вы получите 99,9% от пути. Что-то вроде этого должно работать:
srand((time(NULL) & 0xFFFF) | (getpid() << 16));
Вы также можете читать данные из систем /dev/urandom
on * nix, но в Windows это не эквивалентно.
Ответ 4
unsigned seed;
read(open("/dev/urandom", O_RDONLY), &seed, sizeof seed);
srand(seed); // IRL, check for errors, close the fd, etc...
Я бы также рекомендовал лучший генератор случайных чисел.
Ответ 5
Если С++ 11 можно использовать, рассмотрим std::random_device
. Я предлагаю вам посмотреть ссылку для подробного руководства.
Извлечение существенного сообщения из видеосвязи: вы никогда не должны использовать srand
и rand
, но вместо этого используйте std::random_device
и std::mt19937
- для большинства случаев вам понадобится следующее:
#include <iostream>
#include <random>
int main() {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist(0,99);
for (int i = 0; i < 16; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
Ответ 6
Вместо прямого времени, измеренного в секундах от функции C std lib time(), вы могли бы вместо этого использовать счетчик процессоров? У большинства процессоров есть счетчик свободного пробега, например, в x86/x64 есть счетчик времени печати:
Счетчик временных меток - это 64-разрядный регистр, присутствующий на всех процессорах x86 с Pentium. Он подсчитывает количество тиков с reset.
(Эта страница также имеет множество способов доступа к этому счетчику на разных платформах - gcc/ms visual c/etc)
Имейте в виду, что счетчик времени не имеет недостатков, он может не синхронизироваться между процессорами (вы, вероятно, не заботитесь о своем приложении). И функции энергосбережения могут увеличивать или уменьшать процессор (опять вам, вероятно, все равно).
Ответ 7
Просто идея... сгенерирует GUID (который составляет 16 байт) и суммирует его 4-байтные или 8-байтовые фрагменты (в зависимости от ожидаемой ширины семени), позволяя целочисленное обертывание. Используйте результат как семя.
GUID обычно инкапсулируют характеристики компьютера, сгенерированного ими (например, MAC-адрес), что должно сделать невероятным то, что две разные машины будут создавать одну и ту же случайную последовательность.
Это, очевидно, не переносимо, но найти подходящие API/библиотеки для вашей системы не должно быть слишком сложно (например, UuidCreate
в Win32, uuid_generate
в Linux).
Ответ 8
Окна
Предоставляет CryptGenRandom()
и RtlGenRandom()
. Они дадут вам массив случайных байтов, которые вы можете использовать в качестве семян.
Вы можете найти документы на msdn pages.
Linux/Unixes
Вы можете использовать Openssl RAND_bytes()
, чтобы получить случайное число байтов в linux. Он будет использовать /dev/random
по умолчанию.
Объединение:
#ifdef _WIN32
#include <NTSecAPI.h>
#else
#include <openssl/rand.h>
#endif
uint32_t get_seed(void)
{
uint32_t seed = 0;
#ifdef _WIN32
RtlGenRandom(&seed, sizeof(uint32_t) );
#else
RAND_bytes(&seed, sizeof(uint32_t) );
#endif
return seed;
}
Обратите внимание, что openssl предоставляет криптографически безопасный PRNG по умолчанию, поэтому вы можете использовать его напрямую. Подробнее здесь.
Ответ 9
Предполагая, что вы находитесь в разумной системе POSIX-ish, вы должны иметь clock_gettime
. Это даст текущее время в наносекундах, что означает, что для всех практических целей невозможно получить одно и то же значение дважды. (Теоретически плохие реализации могут иметь гораздо более низкое разрешение, например, просто умножая миллисекунды на 1 миллион, но даже наполовину приличные системы, такие как Linux, дают реальные результаты наносекунды.)
Ответ 10
Если уникальность важна, вам нужно организовать для каждого node информацию о том, какие идентификаторы были заявлены другими. Вы могли бы сделать это с помощью протокола, в котором "кто-то утверждал ID x?" или заранее организовать для каждого node выбор идентификаторов, которые не были выделены другим пользователям.
(GUID используют MAC-адрес машины, поэтому попадают в категорию "организовать заранее".)
Без какой-либо формы соглашения вы рискуете двумя узлами, которые заходят в один и тот же идентификатор.