Подтвердить что ты не робот

Есть ли альтернатива использованию времени для генерации генерации случайных чисел?

Я пытаюсь запустить несколько экземпляров кода (2000 экземпляров или около того) одновременно в вычислительном кластере. Способ его работы заключается в том, что я отправляю задания, и кластер будет запускать их, поскольку узлы открываются так часто, с несколькими заданиями на node. Это, похоже, дает одинаковые значения для большого числа экземпляров в их генерации случайных чисел, в которых используется время-семя.

Есть ли простая альтернатива, которую я могу использовать вместо этого? Воспроизводимость и безопасность не важны, быстрое генерирование уникальных семян. Какой был бы самый простой подход к этому, и, если возможно, кросс-платформенный подход был бы хорошим.

4b9b3361

Ответ 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-адрес машины, поэтому попадают в категорию "организовать заранее".)

Без какой-либо формы соглашения вы рискуете двумя узлами, которые заходят в один и тот же идентификатор.