2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу Пред.  1, 2, 3, 4, 5, 6  След.
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение19.12.2025, 01:11 
realeugene в сообщении #1712827 писал(а):
Период младшего бита $2^{32}$ для многих приложений более чем приемлем.

Зачем вообще в большинстве случае вообще думать, приемлем такой дефект генератора или нет, если сразу нормальный и при этом тоже простой алгоритм можно взять? К тому же крошечным периодом 2^{32} младших бит дефекты классического LCG64 с выводом старших 32 бит явно не исчерпываются: он с треском проваливает тест Birthday Spacings в 2D всеми старшими 32 битами. А такой провал - это грубый статистический дефект, говорящий о том, что ГПСЧ не подчиняется равномерному распределению и рисует какие-то решетки или узоры. Для численных методов такой ГПСЧ надо не рассуждая "выкрасить и выбросить".

К тому же MWC64X ещё и дружественный к 32-битным платформам, нужные для его эффективной реализации машинные инструкции на x86 есть уже 40 лет.

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение20.12.2025, 01:12 
Аватара пользователя
Правильно ли я понимаю, что один из тестов на качество ГПСЧ заключается в том, чтобы найти период через который воспроизводится битовая последовательность каждый бит которой получен, как какой-то один (например младший) бит этого самого псевдо случайного числа? У плохого генератора эти биты будут воспроизводиться с маленьким периодом, что-то навроде вот такого: 01010101. А с хорошим генератором битовая последовательность из этих битов длиной $K$ случайно воспроизведётся скорее всего не сильно раньше чем через $2^{K}$ последовательных генераций?

Я написал простенькую программку которая "ищет иголку в стоге сена". Иголкой является последовательность из 48 битов полученных из какого-либо (например младшего) бита псевдо случайного числа на первых 48 генерациях ГСПЧ. Стогом сена является все последующие последовательные генерации по которым я иду со скользящим окном длиной 48 и ищу в этом окне эту иголку.

Я немного поиграл с линейным конгруэнтным методом и случайно обнаружил, что вот такая простая его модификация даёт какой-то огромадный период, который я не могу дождаться когда ж он наконец сосчитается:
Используется синтаксис C
static inline uint64_t random_uint64(uint64_t *seed)
{
        const uint64_t next_seed = ((*seed) >> 1) * 3818459758128562643ull + 1942700270941896839ull;
        (*seed) = next_seed;
        return next_seed;
}


Программка:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>

static inline uint64_t random_uint64(uint64_t *seed)
{
        const uint64_t next_seed = ((*seed) >> 1) * 3818459758128562643ull + 1942700270941896839ull;
        (*seed) = next_seed;
        return next_seed;
}

int main(int argc, char *argv[])
{
        enum { SHIFT = 0 };
        enum { N = 48 };
        uint64_t seed = 42;
        uint8_t needle[N];
        uint8_t haystack[N];
        if (argc == 2) {
                seed = atoll(argv[1]);
        }
        printf("seed = %" PRIu64 "\n", seed);
        printf("needle = ");
        for (size_t i = 0; i < N; i++) {
                needle[i] = ((random_uint64(&seed) >> SHIFT) & 0x01);
                haystack[i] = 0;
                if (needle[i]) {
                        printf("1");
                } else {
                        printf("0");
                }
        }
        printf("\n");
        uint64_t old_iteration = 0;
        uint64_t hit_count = 0;
        uint64_t iteration = 0;
        while (hit_count < 16) {
                iteration++;
                for (size_t i = 0; i < (N - 1); i++) {
                        haystack[i] = haystack[i + 1];
                }
                haystack[N - 1] = ((random_uint64(&seed) >> SHIFT) & 0x01);
                const bool ok = (*((uint64_t *)&needle[0]) == *((uint64_t *)&haystack[0]))
                        && (*((uint64_t *)&needle[8]) == *((uint64_t *)&haystack[8]))
                        && (*((uint64_t *)&needle[16]) == *((uint64_t *)&haystack[16]))
                        && (*((uint64_t *)&needle[24]) == *((uint64_t *)&haystack[24]))
                        && (*((uint64_t *)&needle[32]) == *((uint64_t *)&haystack[32]))
                        && (*((uint64_t *)&needle[40]) == *((uint64_t *)&haystack[40]));
                if (ok) {
                        hit_count++;
                        printf("[%16" PRIu64 "] needle has been found (period = %" PRIu64 ")\n", iteration, (iteration - old_iteration));
                        old_iteration = iteration;
                }
                if ((iteration % (1024 * 1024 * 1024)) == 0) {
                        printf("[%16" PRIu64 "]\n", iteration);
                }
        }
        return 0;
}
 


-- 20.12.2025, 02:09 --

А, я понял в чём дело. Такая модификация линейного конгруэнтного метода приводит к тому, что оно через некоторое количество генераций забывает начальное состояние и начинает ходить кругами вокруг какого-то аттрактора. На этой орбите исходной иголки нет. Для некоторых начальных значений seed длина замкнутой орбиты 1'591'088'483.

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение20.12.2025, 02:45 
SergeyGubanov в сообщении #1712899 писал(а):
который я не могу дождаться когда ж он наконец сосчитается:

Да, период младшего бита стал больше, но генератор скорее сломан, а не улучшен. Мы ведь теперь не знаем период алгоритма, а ГПСЧ без доказанного периода (или хотя бы вероятности свалиться в плохой цикл не более 10^{-1000}) пользоваться нельзя. У меня после модификации он стал проваливать Gap Test из TAOCP2, чего до этого не делал; похоже, когда мы убираем младший бит x, то делаем преобразование необратимым (что здесь совершенно недопустимо) и резко снижаем период в целом. Изначальная конструкция хотя бы имела доказанный период. Тут лучше идти другим путём: изначальный LCG оставить, но скремблировать выход, как сделано в PCG. Я в ходе написания поста придумал и проверил такой скремблер:

Код:
static inline uint64_t get_bits_raw(void *state)
{
    Lcg64State *obj = state;
    uint64_t out = obj->x ^ (obj->x >> 32);
    out *= 6906969069U;
    out = out ^ rotl64(out, 17) ^ rotl64(out, 53);
    obj->x = 6906969069U * obj->x + 12345U;
    return out;
}


Алгоритм довольно быстрый (0.3 cpb) и совсем грубые артефакты LCG он вроде бы уничтожает, но для реальных дел лучше взять PCG от M.O'Neill. Их хотя бы широко тестировали, чего нельзя сказать о моем наброске. Всерьёз гонять его в PractRand и TestU01 мне его лень :) Тест "64-битный парадокс дней рождения" (не Birthday Spacings) он должен провалить.

P.S. Да, Вы правильно написали про сваливание в аттрактор и крошечный период меньше 2^{32}. Кстати, при этом провалы тестов Birthday Spacings остаются на месте.

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение20.12.2025, 18:44 
Аватара пользователя
Dig386 в сообщении #1712900 писал(а):
Тут лучше идти другим путём: изначальный LCG оставить, но скремблировать выход
Если я правильно понял, что обозначает "скремблировать выход", то вот так получается какой-то немыслимый период:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
static inline uint64_t random_uint64(uint64_t *seed)
{
        enum {
                //SHIFT = 1, // period 2^(1*8+1) = 2^(9)  = 512 (VERIFIED)
                //SHIFT = 2, // period 2^(2*8+1) = 2^(17) = 131'072 (VERIFIED)
                //SHIFT = 3, // period 2^(3*8+1) = 2^(25) = 33'554'432 (VERIFIED)
                //SHIFT = 4, // period 2^(4*8+1) = 2^(33) = 8'589'934'592 (VERIFIED)
                // -----------------------------------------------------------------
                //SHIFT = 5, // period 2^(5*8+1) = 2^(41) = 2'199'023'255'552 (NOT VERIFIED YET)
                //SHIFT = 6, // period 2^(6*8+1) = 2^(49) = 562'949'953'421'312 (NOT VERIFIED YET)
                //SHIFT = 7, // period 2^(7*8+1) = 2^(57) = 144'115'188'075'855'872 (NOT VERIFIED YET)
                // -----------------------------------------------------------------------------------
                SHIFT = 8,   // period 2^(8*8+1) = 2^(65) = 36'893'488'147'419'103'232 (seems to be impossible)
        };

        const uint64_t x0 = (*seed) * 2163025688268396121ull + 7524640194127809971ull;
        (*seed) = x0;

        const uint64_t x1 = (x0 >> SHIFT) * 3818459758128562643ull + 1942700270941896839ull;
        const uint64_t x2 = (x1 >> SHIFT) * 1289501123563855931ull + 7697066845656800123ull;
        const uint64_t x3 = (x2 >> SHIFT) * 8642740071599641433ull + 7995491805778269811ull;
        const uint64_t x4 = (x3 >> SHIFT) * 5496854858494897309ull + 8066340647080867453ull;
        const uint64_t x5 = (x4 >> SHIFT) * 3744020527268025971ull + 6362489920623865711ull;
        const uint64_t x6 = (x5 >> SHIFT) * 8224746936468799673ull + 6705637928504186411ull;
        const uint64_t x7 = (x6 >> SHIFT) * 3166132566178043719ull + 5557380259912464493ull;
        const uint64_t x8 = (x7 >> SHIFT) * 8706947300230443683ull + 1796717567361692861ull;

        return x8;
}


Я это проверил для SHIFT=1, 2, 3, 4.
Для SHIFT=5 проверять надо будет слишком долго.
А вот что происходит при SHIFT=8, так это вообще загадка.

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение20.12.2025, 19:48 
SergeyGubanov в сообщении #1712951 писал(а):
то вот так получается какой-то немыслимый период:

Моё впечатление:
1) Генератор стал лучше проходить многие тесты, но при этом проваливает одномерный тест Birthday Spacings, т.е. для научных расчётов он непригоден. Реализацию теста брал из батареи default пакета тестов SmokeRand.
2) Выходное преобразование не является биекцией, т.е. генератор может выдать не каждое 64-битное значение. Не знаю, хорошо это или плохо, я не тестировал его глубоко.
3) Работа замедлилась до 1.0-1.2 cpb, при этом на моей машине ChaCha8 и Speck128/128 с аппаратным ускорением (AVX2) работают быстрее.

Если хочется действительно хороших скремблеров, то здесь описан один из лучших (только счётчик надо не забыть на 64 бита переделать):
https://datatracker.ietf.org/doc/html/rfc7539
Число раундов ChaCha можно сократить до 8, по умолчанию там 20 раундов.

P.S. Моя поделка прошла BigCrush из TestU01, PractRand до 2 ТиБ (может, и больше сможет, не проверял) и батарею full из SmokeRand (но в этом пакете она ожидаемо проваливает специальную батарею, проверяющую отсутствие дубликатов в 64-битных значениях).

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение21.12.2025, 22:38 
Аватара пользователя
Подсчитал несколько центральных моментов распределений случайных чисел получаемых из Вашего и из моего алгоритмов. Смотрел на младшие 25 битов, сгенерировал числа $256 \times 2^{25}$ раз так чтобы среднее количество генераций каждого из $2^{25}$ чисел было 256.0 (при условии, что генератор даёт равномерное распределение). И вот что получилось. Все центральные моменты до 6-го совпали:
$$
\begin{array}{ccc}
\text{Moments} & (\text{Distribution} 1) & (\text{Distribution} 2) \\
\text{Min} & 167 & 166 \\
\text{Max} & 349 & 352 \\
\text{Avr} & 256.0 & 256.0 \\
\mu_{2} & 16.0 & 16.0 \\
\mu_{3} & 6.35 & 6.36 \\
\mu_{4} & 21.07 & 21.07 \\
\mu_{5} & 14.56 & 14.57 \\
\mu_{6} & 25.16 & 25.16
\end{array}
$$
Здесь я извлёк корень $k$-той степени из традиционного определения $\mu_{k}$ чтобы числа были не очень большими:
$$
\mu_{k} = \left( \frac{1}{N} \sum_{i=1}^{N} \left( x_{i} - \text{Avr}(x) \right)^{k} \right)^{\frac{1}{k}}. 
$$
Меня мучает следующий вопрос. Если оба распределения случайных величин до 6-го центрального момента одинаковы, то что такого видит этот самый Birthday Spacings, что одно распределение ему нравится, а другое нет? И что конкретно означает "для научных расчётов он непригоден" если распределения фактически одинаковые?

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение21.12.2025, 23:24 
SergeyGubanov в сообщении #1713091 писал(а):
то что такого видит этот самый Birthday Spacings

Он делает следующее: берет выборку из m элементов (беззнаковых целых чисел, возвращаемых ГПСЧ), каждый из которых потенциально может принимать n значений. Нередко n равно 2^{32} или 2^{64}. Затем делается следующее:
1) Выборка сортируется по возрастанию, считается m - 1 разностей соседних элементов в отсортированной выборке.
2) m - 1 разностей снова сортируются по возрастанию.
3) В отсортированных разностях ищутся одинаковые значения. Число одинаковых пар значений приблизительно (асимптотически) подчиняется распределению Пуассона с \lambda = \frac{m^3}{4n}, обычно \lambda = 4 или 8. Для повышения чувствительности тест повторяют от десятков до тысяч раз, складывают ожидаемые значения и проверяют распределение суммы (другой вариант - сравнивают с распределением Пуассона по критерию хи-квадрат). Если p-значение выпадает из интервала (10^{-10}; 1 - 10^{-10}), то тест считается проваленным.

Работа с описанием теста: https://doi.org/10.18637/jss.v007.i03

У теста есть многомерные модификации, в которых значения набирают из старших или младших бит генератора. Их довольно много как в TestU01, так и в SmokeRand. Провал теста означает, что точки ложатся каким-то узором, обычно решетчатым. Тут дело в том, что Ваш скремблер основан на той же математике, что и сам LCG, т.е. умножениях и сложениях. И характерные для LCG решетки просто растягиваются и смещаются и, возможно, размываются, но не исчезают. В моем варианте я более активно использую "другую математику", т.е. побитовые операции и повороты.

Цитата:
И что конкретно означает "для научных расчётов он непригоден" если распределения фактически одинаковые?

Означает, что такая решетчатая структура может искажать какое-нибудь интегрирование или построение марковских цепей по Монте-Карло, оценку квантилей распределений по Монте-Карло и т.п. При систематическом провале теста Birthday Spacings проще не вдумываться, а сразу сказать "стоп, это игрушка", и выбросить генератор.

Цитата:
если распределения фактически одинаковые

Случайная равномерно распределенная последовательность подразумевает не только формальное соответствие равномерному распределению, но и несжимаемость (см. колмогоровскую сложность). Разумеется, вывод ГПСЧ имеет низкую колмогоровскую сложность, т.к. производится короткой программой. Но наилучший способ аппроксимировать высокую колмогоровскую сложность - это конструировать ГПСЧ так, чтобы человечеству не был известен способ сжать его вывод без полного перебора сидов, а сиды - достаточно длинны, чтобы это было бы затруднительно даже со сферой Дайсона. Такие ГПСЧ называются поточным шифрами. Остальное - полумеры.

P.S. Проходит ли Ваши тесты на моменты такой генератор: x_{n+1} = x_{n} + \mathrm{CAFEBABEDEADBEEF}_{16} \mod 2^{64} (т.н. Discrete Weyl Sequence)?

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение22.12.2025, 02:02 
Аватара пользователя
Dig386 в сообщении #1713092 писал(а):
P.S. Проходит ли Ваши тесты на моменты такой генератор: x_{n+1} = x_{n} + \mathrm{CAFEBABEDEADBEEF}_{16} \mod 2^{64} (т.н. Discrete Weyl Sequence)?
Оно ж не "шумит". Отклонения от среднего отсутствуют.

Минимальная проверка на вшивость: у "дробового шума" дисперсия должна быть $\sqrt{N}$.

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение22.12.2025, 02:31 
SergeyGubanov в сообщении #1713097 писал(а):
Оно ж не "шумит". Отклонения от среднего отсутствуют.

Оказалось, что шумит. Вот такая программа выдаёт среднее и стандартное отклонение для равномерного распределения, близкие к теоретическим. Хотя в роли ГПСЧ - "дискретная последовательность Вейля":

код: [ скачать ] [ спрятать ]
Используется синтаксис C
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

int main()
{
    const size_t n = 10000;
    uint64_t w = (uint64_t) time(NULL);
    double *x = calloc(n, sizeof(double));
    double mean = 0.0, std = 0.0;
    for (size_t i = 0; i < n; i++) {
        w += 0xCAFEBABEDEADBEEF;
        x[i] = (double) w * 5.421010862427522e-20;        
        mean += x[i];
    }
    mean /= (double) n;
    for (size_t i = 0; i < n; i++) {
        const double d = x[i] - mean;
        std += d * d;
    }
    std = sqrt(std / (double) (n - 1));
    free(x);
    printf("mu = %g; std = %g\n", mean, std);
    return 0;
}
 

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение22.12.2025, 23:26 
Аватара пользователя
А, ну в этом смысле шумит.

Я имел в виду фотонный шум в матрице фотокамеры при малом числе фотонов. В каждый пиксель матрицы попадает в среднем $N$ фотонов с дисперсией $\sqrt{N}$. При недостатке освещения фотография оказывается сильно зашумлена. То же самое распределение будет с каплями дождя по вёдрам. Я поставил $2^{25}$ вёдер и пролил на них дождь из $256 \times 2^{25}$ капель. В каждое ведро в среднем попало по 256 капель. Но из-за дробового шума, в некоторые вёдра попало капель больше, а в некоторые меньше. Квадратичное отклонение $\sqrt{256} = 16$.

А если генерировать капли по формуле $x_{n+1} = x_{n} + C$, то во все вёдра накапает в точности по 256 капель, с нулевой дисперсией, без "шума".

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение23.12.2025, 00:38 
SergeyGubanov в сообщении #1713166 писал(а):
то во все вёдра накапает в точности по 256 капель, с нулевой дисперсией, без "шума".

Статистические тесты для ГПСЧ как раз и ищут подобные регулярности, но более тонкого характера. Но они не очень похожи на статистические критерии из курсов теории вероятности и математической статистики, т.к. ищут не просто отклонения от равномерного распределения, а оставшиеся от формул закономерности и артефакты.

Кстати, непонимание природы того "шума", о котором Вы говорите, приводит к порой весьма странным работам о ГПСЧ. Вот пример:

https://doi.org/10.31857/S0044466920110046

Авторы из-за случайных флуктуаций в заполнении псевдослучайными точками плоскости делают далеко идущие выводы о ненадёжности всех испытанных ими ГПСЧ, даже таких качественных, как ThreeFry и Philox. Примечательно, что при получении такого результата авторы даже не попытались перепроверить работу с помощью криптографических генераторов, а также провести теоретический анализ. И даже говорят, что "В настоящее время вряд ли существует какой-то алгоритм построения псевдослучайных чисел, который можно с полной уверенностью использовать для расчета любых случайных процессов.", хотя AES и ChaCha к этому крайне близки. А криптоанализ как один из подходов к контролю качества ГПСЧ даже не упоминается.

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение23.12.2025, 14:43 
SergeyGubanov в сообщении #1713166 писал(а):
А если генерировать капли по формуле $x_{n+1} = x_{n} + C$
Смотря как вы эти 64-битные числа превратите в капли. Если сначала пропустите такие числа через криптографическую хэш-функцию, вы получите неплохой ГПСЧ. У вас в дожде всего $2^{33}$ капель, а период битовой последовательности аж $2^{70}$.

-- 23.12.2025, 14:54 --

Dig386 в сообщении #1713171 писал(а):
Но они не очень похожи на статистические критерии из курсов теории вероятности и математической статистики, т.к. ищут не просто отклонения от равномерного распределения, а оставшиеся от формул закономерности и артефакты.
Если артефакты приходится искать, значит, они не заметны, и в большинстве случаев стоит не тратить время на глубокое разбирательство с ними и пользоваться библиотечными генераторами. А как наука это всё, конечно, любопытно. Не просветите, какие сейчас популярные пакеты тестов и сколько времени занимает тестирование на ноуте?

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение23.12.2025, 19:51 
realeugene в сообщении #1713218 писал(а):
Не просветите, какие сейчас популярные пакеты тестов и сколько времени занимает тестирование на ноуте?

Наиболее популярные пакеты тестов - это TestU01 и PractRand. Первый из них работает от десяти секунд (SmallCrush) до нескольких часов (BigCrush). Время работы второго зависит от тестируемой выборки, по умолчанию там 32 ТиБ с показом промежуточных результатов, занимает часов 30. Есть ещё Dieharder, выполняющийся около часа, но он значительно менее чувствительный, чем первые два пакета. Не так давно появился SmokeRand, напоминающий TestU01, выполняющийся минут за 20, и при этом умеющий также грузить экспериментальную многопоточную модификацию TestU01. Там BigCrush занимает уже менее часа.

Цитата:
Если артефакты приходится искать, значит, они не заметны

Зависит от задачи, иногда они могут проявиться неожиданным и коварным образом. А если "иногда", то проще пессимистично считать, что "иногда" - это "всегда". Особенно с учетом того, что хорошие алгоритмы могут быть быстрыми и очень простыми.

Цитата:
Если сначала пропустите такие числа через криптографическую хэш-функцию, вы получите неплохой ГПСЧ.

Так можно делать, но поточные шифры AES-CTR или ChaCha12 будут в обычно в несколько раз быстрее. Они как раз шифруют значение счетчика.

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение24.12.2025, 00:36 
Аватара пользователя
Dig386, а Вы случайно не пробовали использовать инструкцию реверсной перестановки битов? ARM RBIT?

Идея в том, что у LCG младшие биты являются "слабыми". Если сделать битовый reverse, то младшие поменяются со старшими и теперь младшие биты станут "сильными". Если теперь поксорить число со слабыми младшими битами, но сильными старшими битами с числом у которого всё наоборот, то все биты станут "нормальными".

Например, как-то так (x - имеет слабые младшие биты, y - имеет слабые старшие биты):
Используется синтаксис C
static inline uint64_t random_uint64(uint64_t *seed)
{
        const uint64_t x = (*seed) * 2163025688268396121ull + 7524640194127809971ull;
        (*seed) = x;
        const uint64_t y = rbit64(rbit64(x) * 3818459758128562643ull + 1942700270941896839ull);
        return x ^ y;
}


RBIT:
Используется синтаксис C
static inline uint64_t rbit64(uint64_t x)
{
#if HAS_RBIT
        return __rbitll(x);
#else
        x = ((x & 0xFFFFFFFF00000000) >> 32) | ((x & 0x00000000FFFFFFFF) << 32);
        x = ((x & 0xFFFF0000FFFF0000) >> 16) | ((x & 0x0000FFFF0000FFFF) << 16);
        x = ((x & 0xFF00FF00FF00FF00) >> 8) | ((x & 0x00FF00FF00FF00FF) << 8);
        x = ((x & 0xF0F0F0F0F0F0F0F0) >> 4) | ((x & 0x0F0F0F0F0F0F0F0F) << 4);
        x = ((x & 0xCCCCCCCCCCCCCCCC) >> 2) | ((x & 0x3333333333333333) << 2);
        x = ((x & 0xAAAAAAAAAAAAAAAA) >> 1) | ((x & 0x5555555555555555) << 1);
        return x;
#endif
}

 
 
 
 Re: О критериях качества генераторов псевдослучайных чисел
Сообщение24.12.2025, 01:34 
SergeyGubanov в сообщении #1713276 писал(а):
Например, как-то так (x - имеет слабые младшие биты, y - имеет слабые старшие биты):

Ради интереса "скормил" выход генератора фреймворку SmokeRand, превратив его в такой плагин:

код: [ скачать ] [ спрятать ]
Используется синтаксис C
#include "smokerand/cinterface.h"

PRNG_CMODULE_PROLOG

static inline uint64_t rbit64(uint64_t x)
{
#if HAS_RBIT
        return __rbitll(x);
#else
        x = ((x & 0xFFFFFFFF00000000) >> 32) | ((x & 0x00000000FFFFFFFF) << 32);
        x = ((x & 0xFFFF0000FFFF0000) >> 16) | ((x & 0x0000FFFF0000FFFF) << 16);
        x = ((x & 0xFF00FF00FF00FF00) >> 8) | ((x & 0x00FF00FF00FF00FF) << 8);
        x = ((x & 0xF0F0F0F0F0F0F0F0) >> 4) | ((x & 0x0F0F0F0F0F0F0F0F) << 4);
        x = ((x & 0xCCCCCCCCCCCCCCCC) >> 2) | ((x & 0x3333333333333333) << 2);
        x = ((x & 0xAAAAAAAAAAAAAAAA) >> 1) | ((x & 0x5555555555555555) << 1);
        return x;
#endif
}


static inline uint64_t get_bits_raw(Lcg64State *state)
{
    const uint64_t x = state->x * 2163025688268396121ull + 7524640194127809971ull;
    state->x = x;
    const uint64_t y = rbit64(rbit64(x) * 3818459758128562643ull + 1942700270941896839ull);
    return x ^ y;
}


static void *create(const CallerAPI *intf)
{
    Lcg64State *obj = intf->malloc(sizeof(Lcg64State));
    obj->x = intf->get_seed64();
    return obj;
}

MAKE_UINT64_PRNG("LCG64str", NULL)
 


Результаты оказались плачевными, даже байты не распределены равномерно, старший бит стал плохим, младшие биты рисуют какую-то решетку. Не наделал ли я ошибок при написании плагина?

Код:
==================== 'express' battery report ====================
Generator name:    LCG64str
Output size, bits: 64
Used seed:         _01_hmq5VqZHizDd7TwtVlXAZIx63T6+17HEw5BMyBGg/qI=

    # Test name                    xemp              p Interpretation  Thr#
-------------------------------------------------------------------------------
    1 byte_freq                 16.0104      4.49e-223 FAIL               0
    2 bspace32_1d                  4063          0.694 Ok                 0
    3 bspace8_4d                   1037          0.335 Ok                 0
    4 bspace4_8d                    507          0.576 Ok                 0
    5 bspace4_8d_dec               3592              0 FAIL               0
    6 linearcomp_high                 1              0 FAIL               0
    7 linearcomp_low               5000          0.667 Ok                 0
-------------------------------------------------------------------------------
Passed:        4
Suspicious:    0
Failed:        3
Quality (0-4): 0.00 (very bad)
Elapsed time:  00:00:00
Used seed:     _01_hmq5VqZHizDd7TwtVlXAZIx63T6+17HEw5BMyBGg/qI=

При попытке отправить вывод генератора в PractRand он провалился на 16 КиБ. И главное - на моём x86-64 эта конструкция работает медленнее ChaCha8 и сопоставима по скорости с AES128. И если речь про ARMы, то на них должны быть готовые инструкции для построения хороших ГПСЧ. Например, вот эти:
https://en.wikipedia.org/wiki/AES_instruction_set
Или вот эти, если хочется разнообразия алгоритмов:
https://habr.com/ru/articles/548698/

 
 
 [ Сообщений: 89 ]  На страницу Пред.  1, 2, 3, 4, 5, 6  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group