LINUX.ORG.RU

ГПСЧ кросс-платформенно

 ,


0

2

Нужно на C получить случайное число.

Если использовать rand(), предварительно использовав srand(time(NULL)) в течение одной секунды (а может и больше) в разных процессах «выпадают» абсолютно одинаковые значения.

Читать из файлов вроде /dev/urandom – не вариант. Софт потенциально должен работать на фороточках, хоть и сейчас пишется под UNIX-подобные ОС.

★★★
Ответ на: комментарий от zx_gamer

Вот она и проблема, srand инициализируется секундами, и за 1 секунду несколько раз несколько процессов получают одинаковую точку отсчёта. Уже выше сказали что можно не секунды взять для инициализации зерна, а наносекунды например.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[])
{
    struct timespec t;
    clock_gettime(CLOCK_REALTIME, &t);
    srand(t.tv_nsec);

    printf("%zu\n",random());

    return 0;
}

Так ты повысишь разрешение времени и два процесса запускаемые друг за другом не будут получать одно и тоже зерно в секундах с которого начнётся генерация случайного числа.

Но если твой CGI прям вжухх быстрый там различия будут в десяток наносекунд и всё… Я фиг знаю пойдёт или нет такое. В смысле нужна ли большая разница в случайных значениях или нет. Опять же на правах мыслей вслух если чво :3

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 3)

Софт потенциально должен работать на фороточках

В форточках есть свой вариант надежных случайных чисел (https://stackoverflow.com/questions/191335/windows-equivalent-of-dev-random)

Сделай у себя по #ifdef выбор источника рандома в зависимости от системы.

Второй способ: сделай свой thread local генератор случаных чисел и сидируй его время + №треда + №процесса. Если тебе не нужна криптостойкость, то это проще чем ты думаешь. Например так что-то типа такого:

struct Rnd {
        int u;
        int v;
}

int gen(Rnd *rnd) {
        rnd -> v = 36969 * (rnd -> v & 65535) + (rnd -> v >> 16);
        rnd -> u = 18000 * (rnd -> u & 65535) + (rnd -> u >> 16);
        return (rnd -> v << 16) + (rnd -> u & 65535)
}
Aswed ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

clock_gettime(CLOCK_REALTIME, &t);

Едрит твою. Воя я выше выдал пример через getrandom. Нет, один хрен надо брать seed из времени. У вас что, у всех MAN удалён?

GETRANDOM(2)                     Linux Programmer's Manual                     GETRANDOM(2)

NAME
       getrandom - obtain a series of random bytes

SYNOPSIS
       #include <sys/random.h>

       ssize_t getrandom(void *buf, size_t buflen, unsigned int flags);

DESCRIPTION
       The  getrandom()  system  call  fills the buffer pointed to by buf with up to buflen
       random bytes.  These bytes can be used to seed user-space random  number  generators
       or for cryptographic purposes.

       By default, getrandom() draws entropy from the urandom source (i.e., the same source
       as the /dev/urandom device).  This behavior can be changed via the flags argument.

       If the urandom source has been initialized, reads of up to 256 bytes will always re‐
       turn  as  many  bytes  as requested and will not be interrupted by signals.  No such
       guarantees apply for larger buffer sizes.  For example, if the call  is  interrupted
       by a signal handler, it may return a partially filled buffer, or fail with the error
       EINTR.

       If the urandom source has not yet been initialized, then getrandom() will block, un‐
       less GRND_NONBLOCK is specified in flags.

       The flags argument is a bit mask that can contain zero or more of the following val‐
       ues ORed together:

       GRND_RANDOM
              If this bit is set, then random bytes are drawn from the random source (i.e.,
              the  same  source  as  the /dev/random device) instead of the urandom source.
              The random source is limited based on the entropy that can be  obtained  from
              environmental  noise.   If the number of available bytes in the random source
              is less than requested in buflen, the call returns just the available  random
              bytes.   If  no random bytes are available, the behavior depends on the pres‐
              ence of GRND_NONBLOCK in the flags argument.

       GRND_NONBLOCK
              By default, when reading from the random source,  getrandom()  blocks  if  no
              random  bytes  are  available,  and  when reading from the urandom source, it
              blocks if the entropy pool has not yet been initialized.   If  the  GRND_NON‐
              BLOCK  flag  is  set, then getrandom() does not block in these cases, but in‐
              stead immediately returns -1 with errno set to EAGAIN.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

ТС сказал кроссплатформенно, откуда я знаю насколько и чем и с чем он собирает под винду с mingw с/под msys или без (оно там вообще есть? Я оч давно лишь тыкал). Слишком мало информации. Ему уже openssl предлагали с собой таскать, не хочет. Хотя у него сеть, наверняка оно или подобное рядом лежит. Ну если либу мелкую не хочет то может собирает мелкософт компилятором под виндой. Я лишь предложил, что делать пусть ТС думает, вариантов уже больше десятка всем миром предложили.

Твой вариант надо idef’ать или собирать с чем то что предоставляет это. А тащить он ничего не хочет.

Чво на меня ругаися!! :3 Агрььъ.

А так пусть собирает под никсы и всё, хтьфу на эту вянду и проблем сразу никаких всё будет из коробки. =)

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

А так пусть собирает под никсы и всё, хтьфу на эту вянду и проблем сразу никаких всё будет из коробки. =)

Ну я пока так и сделал #ifdef UNIX

zx_gamer ★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

Чво на меня ругаися!!

Извиняюсь.

Реакция на srand(time(NULL)), который идёт из учебных примеров с пометкой (НИКОГДА ТАК НЕ ДЕЛАЙТЕ В РЕАЛЬНЫХ ПРОЕКТАХ). Но один фиг так делают.

А используют, т.к. нет единого рецепта, а тащить чужую библиотеку западло.

В UNIX/Linux давно решено. Есть getrandom, random, drand48, lrand48, …

Остальные, кто в лес, кто по дрова. Достаточно посмотреть https://github.com/rust-random/getrandom – вот и готовый рецепт для почти всех платформ получить seed.

С rand под виндой, тоже не всё так радужно. Аля, костыли вида :

x = rand() * rand();

В итоге, получаем извечный совет «возьмите исходники drand48 из FreeBSD, так даже Apple сделал».

Отдельная песня с C++11 std::random_device который подосрал mingw. А так было бы реальное универсальное решение. (З.Ы. давно не проверял исправился ли mingw)

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Например ISO/IEC 9899:1999 или ISO/IEC 9899:1990. Даже в glibc функции getrandom и getentropy появились в версии 2.25 от 2017 года. Если кто-то решил прикрутить к ядру свой собственный системный вызов и дёргать его из своей реализации стандартной библиотеки игнорируя стандарт языка, то код, который использует эти функции не будет переносимым и будет полагаться на милость разрабочиков других операционных систем и стандратных библиотек C которые возможно захотят предоставить свою реализацию.

dsl
()
Последнее исправление: dsl (всего исправлений: 1)
Ответ на: комментарий от AlexVR

потому что обсуждают кроссплатформенные способы. getrandom - линуксовый сисколл, причем достаточно новый (а в glibc его еще позже завезли), т.ч. у всяких динозавров его легко может не оказаться.

Lrrr ★★★★★
()
Ответ на: комментарий от dsl

Например ISO/IEC 9899:1999

Причём тут стандарт на Си и заголовки из sys?

который использует эти функции не будет переносимым и будет полагаться на милость разрабочиков других операционных систем

И что, писать только учебные helloworld-ы?

Работа со случайными числами предполагает:

  1. Запросить seed у ОПЕРАЦИОННОЙ СИСТЕМЫ.
  2. Выбрать метод ГПСЧ.
  3. Выбрать нужное распределение.

srand(time(NULL)) и rand() – не имеют никакого прикладного использования, кроме учебных примеров.

Пункт 1 – это единственный способ получить «случайное» число.

Пункты 2-3 зачастую вшиваются в программу (например, игру), если нужна повторяемость на разных платформах (например, генерация мира в игре).

AlexVR ★★★★★
()
Ответ на: комментарий от Lrrr

т.ч. у всяких динозавров его легко может не оказаться

Хватит заниматься классической преждевременной оптимизацией. И говорить о динозаврах, которых давно нет.

Seed НАДО СПРАШИВАТЬ У ОПЕРАЦИОННОЙ СИСТЕМЫ. И ЕСТЕСТВЕННО ЧТО КОД БУДЕТ НЕ ПОРТИРУЕМЫМ. И ТС уже согласился на ifdef. Как спросить у какой ОС seed описано тут https://docs.rs/getrandom/latest/getrandom/ смотрим табличку.

З.Ы. И в этой табличке сказано как спрашивать у динозавров, в том числе.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Причём тут стандарт на Си и заголовки из sys?

Притом, что sys/random.h это заголовочный файл glibc, но не ISO C. Обратите особое внимание на раздел «Проблемы переносимости решённые glibc».

А также быстрый поиск по sys/random.h подсказывает, что не всё так радужно с компиляцией исходников где указан такой заголовочный файл: раз, два, три, четыре.

Работа со случайными числами предполагает:

Запросить seed у ОПЕРАЦИОННОЙ СИСТЕМЫ. Выбрать метод ГПСЧ. Выбрать нужное распределение.

(а) А если вдруг операционная система «из коробки» не умеет давать случайное зерно? Тогда куча способов как это зерно добыть. Замечу, что я не говорю, что clock_gettime это решение всех проблем и его надо использовать всегда. Просто в данном конкретном случае для процессов, которые появляются по запросам из сети его может быть достаточно чтобы давать разные значения зерна при условии, что минимальное время между обработкой соседних запросов больше чем гранулярность clock_gettime. Иной раз и младшие разряды АЦП можно взять для этих целей если совсем руки связаны.

(б) Выбор метода ГПСЧ и распределения это очень хорошо, но только если описано в стандарте С хотя бы 99 года потому, что будет очень тяжело сейчас наткнуться на компилятор, который его не поддерживает (даже супер-дорогие коммерческие компиляторы под редкие архитектуры).

dsl
()
Ответ на: комментарий от AlexVR

Хватит заниматься классической преждевременной оптимизацией. И говорить о динозаврах, которых давно нет.

Хватит лениться включать голову чтобы писать переносимый код.

dsl
()
Ответ на: комментарий от dsl

А если вдруг операционная система «из коробки» не умеет давать случайное зерно?

КАКАЯ? Пример будет? Или деванные теоретики? Даже из пачки linux-устройств, с которыми я работал, urandom отдавали все!!! Про x86 и говорить нечего.

За всё время, я сталкивался только с необходимостью поставить haveged для решения проблемы с программами считывающих из /dev/random вместо /dev/urandom.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Плиз, ваш вариант кросплатформенного получения seed.

Описан выше.

З.Ы. что у нас с clock_gettime в разных ОС?

Хм, действительно, clock_gettime описана не в ISO C, а в POSIX. Тем не менее, у меня под рукой есть компиляторы gcc-9.2-arm64-eabi и TASKING TriCore6.2r2. Вот что говорит grep: gcc и tasking tricore

dsl
()
Ответ на: комментарий от AlexVR

Например MICROSAR (реализация AUTOSAR от Vector) не умеет просто так генерировать истинно случайное число. Для этого нужна настройка задачи для CSM (cryptographic service module, если правильно помню). Мне например несколько раз дёрнуть АЦП и взять младшие биты при инициализации было проще (а главное быстрее) по сравнению с CSM там, где не требовалась криптографическая стойкость.

UPD: «быстрее» значит, что CSM долго инициализируется и если его дожидаться, то время запуска всего устройства увеличится.

dsl
()
Последнее исправление: dsl (всего исправлений: 1)
Ответ на: комментарий от zx_gamer

Насколько я понял

Запросить seed у ОПЕРАЦИОННОЙ СИСТЕМЫ.

значит, что механизм получения истинно случайного зерна скрыт в реализации операционной системы (функция, системный вызов ядра, etc.)

dsl
()
Последнее исправление: dsl (всего исправлений: 1)
Ответ на: комментарий от AlexVR

Что-то я запутался. Вот что я понял:

rand/srand описаны в ISO/IEC 9899:1999 и в ISO/IEC 9899:1990 как минимум. clock_gettime описана в расширении к ISO C и должна быть доступна на всех системах соответсвующих POSIX.1-2017. getrandom доступна в Linux и glibc начиная с 2017 года, в стандартах не описана.

UPD: clock_settime/clock_gettime/clock_getres были описаны ещё в POSIX Realtime Extension (1003.1b-1993/1003.1i-1995). Теперь я не удивлён тому, почему они поддерживаются TASKING TriCore компилятором.

dsl
()
Последнее исправление: dsl (всего исправлений: 1)
Ответ на: комментарий от dsl

Про rand/srand есть MSC32-C, MSC50-CPP

clock_gettime описана в расширении к ISO C и должна быть доступна на всех системах соответсвующих POSIX.1-2017

random, ?rand48 тоже есть в POSIX, но M$ пофиг.

getrandom доступна в Linux и glibc начиная с 2017 года, в стандартах не описана.

Это не мешает использовать их в стандартных библиотеках других языков программирования. https://github.com/python/cpython/blob/37c5c40125389c71e40b16441fbc72ba01be5fb4/Python/bootstrap_hash.c#L429C9-L429C9

Всего-то:

   Used sources of entropy ordered by preference, preferred source first:

   - BCryptGenRandom() on Windows
   - getrandom() function (ex: Linux and Solaris): call py_getrandom()
   - getentropy() function (ex: OpenBSD): call py_getentropy()
   - /dev/urandom device

Всего 4 варианта получения энтропии в cpython для всех поддерживаемых платформ.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

random, ?rand48 тоже есть в POSIX, но M$ пофиг.

Я думаю, что стоит разделять вопросы формирования стандартов и их реализации компаниями-производителями компиляторов и операционных систем. Тот факт, что clock_gettime в числе прочих была описана ещё в POSIX Realtime Extension (1003.1b-1993/1003.1i-1995) в первой половине 90-х, а MS не посчитала нужным до сих пор реализовать поддержку этого расширения (который с 2017-го года уже часть самого POSIX.1-2017), говорит много о компании.

Это не мешает использовать их в стандартных библиотеках других языков программирования.

С моей точки зрения это опять вопрос реализации стандартных библиотек. Если разработчики python/cpython (не знаю одни это и теже люди или разные) решили, что это отличная идея использовать нестандартную функцию для такой важной вещи как генерация случайных чисел, то так тому и быть. Просто это делает их стандартные библиотека менее переносимыми.

dsl
()
Ответ на: комментарий от AlexVR

что это отличная идея использовать нестандартную функцию для такой важной вещи как генерация случайных чисел, то так тому и быть.

Это вполне себе стандартные функции описанные в документации к Linux, FreeBSD, MacOS, OpenBSD, M$ Windows, … (и блин их не 100500 штук)

Да они не из POSIX, но это уже вопрос к тем, кто упорно поддерживает что-то одно. UNIX группировка даже определиться с поведением /dev/urandom. У них же супер-пупер методы генерации псевдослучайных чисел, с отдельным сервисом сбора энтропии и сохранением энтропии на диск.

AlexVR ★★★★★
()