Заполнение генератора случайных чисел в Javascript

Можно ли заполнить генератор случайных чисел (Math.random ) в JavaScript?


person weepy    schedule 06.02.2009    source источник
comment
неясно, хотите ли вы засеять его, чтобы многократно получать одни и те же результаты для разных тестовых прогонов, или вы хотите засеять его «чем-то уникальным» для каждого пользователя для лучшей случайности между использованием.   -  person simbo1905    schedule 16.05.2014
comment
Нет, к сожалению, это невозможно. jsrand - это небольшая библиотека, которую я написал, когда мне понадобился семенной ГПСЧ. Существуют также другие более сложные библиотеки, которые вы можете найти в поисковой системе.   -  person Domenico De Felice    schedule 02.01.2015
comment
Добавляем к вопросу: как это может быть хорошей идеей предлагать ГПСЧ без средств для его заполнения? Есть ли для этого веская причина?   -  person Alan    schedule 18.07.2018
comment
См. Также stackoverflow.com/questions/424292   -  person Palimondo    schedule 26.03.2020
comment
Вот визуализация некоторых генераторов на этой странице observablehq.com/@tidwall/hello-randomness   -  person tidwall    schedule 15.09.2020


Ответы (16)


Нет, засеять Math.random() невозможно, но довольно легко написать собственный генератор или, что еще лучше, использовать уже существующий.

Проверьте: этот связанный вопрос.

Кроме того, дополнительную информацию о раздаче см. Также в блоге Дэвида Бау.

person PeterAllenWebb    schedule 06.02.2009

Я реализовал ряд хороших, коротких и быстрых функций генератора псевдослучайных чисел (PRNG) на простом JavaScript. Все они могут быть посеяны и дают качественные числа.

Прежде всего, позаботьтесь о том, чтобы правильно инициализировать ваши PRNG. Чтобы упростить задачу, приведенные ниже генераторы не имеют встроенной процедуры генерации начального числа, но принимают одно или несколько 32-битных значений в качестве начальных начальное состояние ГПСЧ. Подобные или редкие начальные числа (например, простое начальное число из 1 и 2) имеют низкую энтропию и могут вызывать корреляции или другие проблемы с качеством, в результате чего выходные данные имеют аналогичные свойства (например, одинаковые случайно сгенерированные уровни). Чтобы избежать этого, рекомендуется инициализировать ГПСЧ с хорошо распределенным начальным значением с высокой энтропией.

К счастью, хеш-функции очень хорошо генерируют начальные числа из коротких строк. Хорошая хеш-функция даст очень разные результаты, даже если две строки похожи. Вот пример генератора начального числа, основанного на функции смешивания MurmurHash3:

function xmur3(str) {
    for(var i = 0, h = 1779033703 ^ str.length; i < str.length; i++)
        h = Math.imul(h ^ str.charCodeAt(i), 3432918353),
        h = h << 13 | h >>> 19;
    return function() {
        h = Math.imul(h ^ h >>> 16, 2246822507);
        h = Math.imul(h ^ h >>> 13, 3266489909);
        return (h ^= h >>> 16) >>> 0;
    }
}

Каждый последующий вызов функции возврата xmur3 создает новое 32-битное хеш-значение, которое будет использоваться в качестве начального числа в ГПСЧ. Вот как это можно использовать:

// Create xmur3 state:
var seed = xmur3("apples");
// Output four 32-bit hashes to provide the seed for sfc32.
var rand = sfc32(seed(), seed(), seed(), seed());

// Output one 32-bit hash to provide the seed for mulberry32.
var rand = mulberry32(seed());

// Obtain sequential random numbers like so:
rand();
rand();

Однако это только одно из возможных решений. В качестве альтернативы, просто выберите фиктивные данные для заполнения начального числа и продвиньте генератор несколько раз (12-20 итераций), чтобы тщательно перемешать начальное состояние. Это часто наблюдается в эталонных реализациях ГПСЧ, но это ограничивает количество начальных состояний:

var seed = 1337 ^ 0xDEADBEEF; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
var rand = sfc32(0x9E3779B9, 0x243F6A88, 0xB7E15162, seed);
for (var i = 0; i < 15; i++) rand();

На выходе этих функций PRNG получается положительное 32-битное число (от 0 до 2 32 -1), которое затем преобразуется в число с плавающей запятой в диапазоне от 0 до 1 (0 включительно, 1 исключающий) эквивалент на Math.random(), если вам нужны случайные числа из определенного диапазона, прочтите эту статью о MDN. Если вам нужны только необработанные биты, просто удалите последнюю операцию деления.

Примечание. Числа JavaScript могут представлять только целые числа с разрешением до 53 бит. А при использовании побитовых операций это число уменьшается до 32. Современные ГПСЧ на других языках часто используют 64-битные операции, для которых при переносе на JS требуются прокладки, которые могут резко снизить производительность. Алгоритмы здесь используют только 32-битные операции, так как они напрямую совместимы с JS.

Теперь перейдем к генераторам. (Я веду полный список со ссылками и информацией о лицензии здесь )


sfc32 (простой быстрый счетчик)

sfc32 является частью пакета тестирования случайных чисел PractRand (который, конечно же, проходит) . sfc32 имеет 128-битное состояние и работает очень быстро в JS.

function sfc32(a, b, c, d) {
    return function() {
      a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0; 
      var t = (a + b) | 0;
      a = b ^ b >>> 9;
      b = c + (c << 3) | 0;
      c = (c << 21 | c >>> 11);
      d = d + 1 | 0;
      t = t + d | 0;
      c = c + t | 0;
      return (t >>> 0) / 4294967296;
    }
}

Шелковица32

Mulberry32 - простой генератор с 32-битным состоянием, но он чрезвычайно быстр и имеет хорошее качество (автор утверждает, что он проходит все тесты gjrand и имеет полный период 2 32, но я не проверял).

function mulberry32(a) {
    return function() {
      var t = a += 0x6D2B79F5;
      t = Math.imul(t ^ t >>> 15, t | 1);
      t ^= t + Math.imul(t ^ t >>> 7, t | 61);
      return ((t ^ t >>> 14) >>> 0) / 4294967296;
    }
}

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

xoshiro128 **

По состоянию на май 2018 г. xoshiro128 ** является новым членом семейства Xorshift от Vigna & Blackman (профессор Винья также отвечал за алгоритм Xorshift128 +, который поддерживает большинство Math.random реализаций под капотом). Это самый быстрый генератор, который предлагает 128-битное состояние.

function xoshiro128ss(a, b, c, d) {
    return function() {
        var t = b << 9, r = a * 5; r = (r << 7 | r >>> 25) * 9;
        c ^= a; d ^= b;
        b ^= c; a ^= d; c ^= t;
        d = d << 11 | d >>> 21;
        return (r >>> 0) / 4294967296;
    }
}

Авторы утверждают, что он хорошо проходит тесты на случайность (хотя и с оговорками). Другие исследователи отметили, что он не проходит некоторые тесты в TestU01 (в частности, LinearComp и BinaryRank). На практике это не должно вызывать проблем при использовании чисел с плавающей запятой (например, в этих реализациях), но может вызвать проблемы, если полагаться на необработанные младшие биты.

JSF (Маленький пост Дженкинса)

Это JSF или smallprng Боба Дженкинса (2007), который также сделал ISAAC и SpookyHash. Он проходит тесты PractRand и должен быть довольно быстро, хотя и не так быстро, как sfc32.

function jsf32(a, b, c, d) {
    return function() {
        a |= 0; b |= 0; c |= 0; d |= 0;
        var t = a - (b << 27 | b >>> 5) | 0;
        a = b ^ (c << 17 | c >>> 15);
        b = c + d | 0;
        c = d + t | 0;
        d = a + t | 0;
        return (d >>> 0) / 4294967296;
    }
}
person bryc    schedule 01.12.2017
comment
Это все включает 0 и не включает 1? - person Chad von Nau; 25.03.2019
comment
Я считаю, что значения, указанные вами в Таблицах линейных конгруэнтных генераторов ... Пьера Лекюера, могут превышать максимальный целочисленный размер в Javascript. Максимальное начальное число (2 ^ 32-1) * 741103597 ≈ 3e18, что больше, чем максимальный размер int в JavaScript ≈ 9e15. Я думаю, что следующие значения из книги Пьера имеют наибольший период в пределах родных границ: seed = (seed * 185852 + 1) % 34359738337. - person Lachmanski; 11.06.2019
comment
@Lachmanski правда, но они связаны 32-битными (и 31-битными Парк-Миллера). Использование Math.imul позволяет ему переполниться, как при умножении в C на 32-битные целые числа. Вы предлагаете LCG, использующую весь диапазон целочисленного пространства JS, что, безусловно, является интересной областью для изучения. :) - person bryc; 11.06.2019
comment
Это круто! Могу я просто скопировать ваш sfc32 в программу LGPL? - person user334639; 08.08.2019
comment
Конечно, не стесняйтесь использовать код для любых целей :) - person bryc; 08.08.2019
comment
Было бы неплохо разместить эти примеры на странице, на которую можно ссылаться без SO в URL-адресе (для указания авторства). - person ; 04.12.2019
comment
@ blobber2 не уверен, что вы имеете в виду, но исходный код взят отсюда (с другими): github.com/bryc/code/blob/master/jshash/PRNGs.md. более или менее суть в репозитории :-) - person bryc; 04.12.2019
comment
спасибо за очень полезный пост! являются ли ››› = 0 и a | = 0 быстрыми способами преобразования в 32-битное int? - person DrLightman; 28.04.2020
comment
@DrLightman правильно - форсирование его на раннем этапе или сохранение его всегда в 32-битном режиме, похоже, ускоряет работу. Хотя сейчас забываю, как именно их мерил. Но в будущем он может легко устареть, поскольку движки JS могут стать умнее. Но даже сейчас этот пример показывает, что хеш fnv1a на 70% медленнее без | 0 в firefox: jsbench.me/afk9jv36oz/ 1. - person bryc; 28.04.2020
comment
Спасибо за исследование, которое вошло в это, bryc! Мне все еще потребовалось немного времени, чтобы понять, что использовать и как сгенерировать целое число 0-n (сначала использовал Math.round, протестировал его, не дал хороших результатов, затем использовал Math.floor, который работал). Может быть, добавить к примеру с яблоками: n = Math.floor(rand() * X); // generate random integer below X? Для выбора алгоритма, например JSF говорит, что он работает хорошо (другие работают без проблем, так лучше?) И должен быть быстрым. Я вижу, что в git у вас есть числа, это может помочь быть более конкретным. Так или иначе, ваш ответ мне помог, спасибо за работу! - person Luc; 01.05.2020
comment
@Luc Спасибо, рад, что помог! Здесь есть несколько обоснованных критических замечаний - я добавлю ссылку на MDN для выполнения ранжированных чисел. JSF и SFC проходят PractRand, но JSF немного медленнее - я просто написал общие наблюдения. Я не хочу, чтобы ответ был СЛИШКОМ длинным. Но я посмотрю, смогу ли я сделать это менее запутанным. :) - person bryc; 01.05.2020
comment
@bryc Я вижу, вы поработали над этим постом! Ссылка на MDN - хорошая идея, я не знал об этой странице (избавил бы меня от проб и ошибок :)). Один вопрос: это все 32-битные генераторы, а чуть позже sfc32 имеет 128-битное состояние. Разве это не противоречие (вероятно, оставшееся от предыдущей ревизии)? - person Luc; 02.05.2020
comment
@ Люк Нет, это правильно. Эти алгоритмы перенесены из 32-битного кода C, предназначенного для старых 32-битных процессоров. 128-битное состояние - это просто размер внутренней памяти и подразумевает набор из четырех 32-битных слов, действующих как одно целое. В JS побитовые операции связаны 32-битными словами, поэтому 32-битный код C может быть эффективно перенесен на JS. Надеюсь, это объясняет - person bryc; 02.05.2020
comment
@bryc Итак, если я правильно понимаю, период будет 2 ^ 128, и все они 32-битные, что означает, что алгоритмы могут быть эффективно перенесены с исходного C на JavaScript? Потому что, как мне кажется, людей волнует период, а не то, насколько тяжелой была работа по портированию. Еще раз спасибо, кстати :) - person Luc; 02.05.2020
comment
@Luc Размер состояния не определяет период, только теоретический максимум. Минимальный период 2 ^ 32 достаточен для некриптографического использования. JSF и SFC имеют средний период 2 ^ 127 (согласно PractRand), что неплохо. И я упоминаю 32-битную версию, потому что это важно для понимания. Что-то вроде PCG по самой своей природе неэффективно. / slow в JS (моя цель - эффективность / скорость). Для переопределения 64-битного умножения требуется больше кода, чем для любого из приведенных выше PRNG, и в конечном итоге он работает экспоненциально медленнее. - person bryc; 02.05.2020
comment
Спасибо за этот код. Я сомневаюсь в использовании xmur3 для инициализации sfc32. Вы инициализируете 128 бит семени только с 32 битами энтропии. Я думаю, что лучше использовать хеш, который дает больший результат. - person Vroo; 18.09.2020
comment
@Vroo Меня это не беспокоит, качество ГПСЧ ›качество семян, и это в некоторой степени неизбежно. Из-за побитовых ограничений JS 32-битные результаты необходимо смешивать. Взгляните на MurmurHash3_x86_128 - четыре несвязанных 32- Достаточно смешанные битовые хэши дают действительные 128-битные хэши. Если улучшенное смешивание хешей компонентов не увеличивает энтропию, как в MurmurHash3_x86_128, то с этим ничего нельзя поделать ~ - person bryc; 18.09.2020
comment
Моя точка зрения @bryc заключалась в том, что вы не используете четыре некоррелированных 32-битных хэша, вы используете четыре коррелированных хэша, поскольку xmur3 имеет только 32 бита внутреннего состояния. Я использую ваш порт sfc32 (спасибо!) И инициализирую его 128-битным хешем. - person Vroo; 06.10.2020
comment
@Vroo xmur3 действительно коррелирован, это нельзя отрицать - я имел в виду связанную реализацию 128-битного хеша, которая не была коррелирована. К сожалению, исправление xmur3 потребовало бы увеличения размера кода в четыре раза. Какой 128-битный хеш вы используете? Я не видел многих из них в JS. - person bryc; 14.10.2020

ПРИМЕЧАНИЕ. Несмотря на (а точнее, из-за) лаконичности и кажущейся элегантности, этот алгоритм никоим образом не является высококачественным с точки зрения случайности. Ищите, например, те, которые перечислены в этом ответе для получения лучших результатов.

(Первоначально адаптировано из умной идеи, представленной в комментарии к другому ответу.)

var seed = 1;
function random() {
    var x = Math.sin(seed++) * 10000;
    return x - Math.floor(x);
}

Вы можете установить seed на любое число, просто избегайте нуля (или любого числа, кратного Math.PI).

На мой взгляд, элегантность этого решения заключается в отсутствии каких-либо «магических» чисел (кроме 10000, что представляет собой минимальное количество цифр, которое вы должны выбросить, чтобы избежать странных шаблонов - смотрите результаты со значениями 10, 100, 1000). Краткость тоже хороша.

Это немного медленнее, чем Math.random () (в 2 или 3 раза), но я считаю, что он примерно так же быстр, как любое другое решение, написанное на JavaScript.

person Antti Kissaniemi    schedule 10.10.2013
comment
Есть ли способ доказать, что этот ГСЧ генерирует числа, которые равномерно распределены? Экспериментально это кажется: jsfiddle.net/bhrLT - person Nathan Breit; 12.10.2013
comment
6 000 000 операций в секунду - это довольно быстро, я не планирую создавать больше чем ~ 3 000 000 за клик. Шучу, это здорово. - person A.M.K; 02.05.2014
comment
На самом деле, чем меньше число, тем быстрее он работает. Десятичное число Math.random составляет ~ 20000000 операций в секунду. Я только что обновил jsperf. В любом случае это, вероятно, не будет узким местом в вашем приложении. - person A.M.K; 02.05.2014
comment
-1, это вовсе не универсальный семплер - он довольно смещен в сторону 0 и 1 (см. jsfiddle. net / bhrLT / 17, что может занять некоторое время). Последовательные значения коррелированы - связаны каждые 355 значений, а тем более каждые 710. Пожалуйста, используйте что-нибудь более продуманное! - person spencer nelson; 22.05.2014
comment
@kharybdis измените 10000 на 1000000, и это выглядит лучше - person Jason Goemaat; 29.05.2014
comment
@JasonGoemaat, это просто берет несколько дополнительных десятичных знаков из функции sin - это не решает основную проблему, заключающуюся в том, что десятичные дроби sin не распределены равномерно. Это выглядит лучше, но это только потому, что вам нужно больше точек данных, чтобы увидеть неоднородность. - person spencer nelson; 31.05.2014
comment
Вопрос не в создании криптографически безопасного генератора случайных чисел, а в том, что работает в javascript, полезно для быстрых демонстраций и т.д. - person Jason Goemaat; 31.05.2014
comment
Глядя на опубликованную гистограмму Спенсера Нельсона, похоже, что однородность можно легко улучшить, отбросив результаты за пределами диапазона, скажем [0,1,0,9], и нормализуя оставшиеся результаты из этого диапазона до [0,1]. - person realh; 23.06.2015
comment
Джейсон Гоэмаат: На самом деле любая операция с модом может соответствовать ответу в этом случае, и в этом отношении только использование sin (x) или mod (x, ...) одинаково. Это не случайное число, вы также можете использовать перестановку, чтобы работать быстрее. Тогда вопрос вводит в заблуждение, если такие уловки принимаются. Я голосую против. Генератор случайных чисел должен показывать ни корреляцию, ни частотную диаграмму, это глупо. - person danbo; 01.09.2015
comment
Будь осторожен. Math.sin () может давать разные результаты на клиенте и сервере. Я использую Meteor (использует javascript на клиенте и сервере). - person Obiwahn; 27.10.2015
comment
@realh Совершенно верно! jsfiddle.net/Llorx/bhrLT/93 По-прежнему не на 100% безопасен для криптографии, но результат более нормализованный. - person Jorge Fuentes González; 17.01.2017
comment
Это создает разные результаты в разных браузерах ... chrome, safari firefox имеют разные результаты. поэтому, если цель состоит в том, чтобы использовать семена для получения однородности, это не сработает. - person Ray Foss; 16.08.2017
comment
Придумать этот ответ таким, какой он есть, неправильно. - person Rz Mk; 29.10.2018
comment
Он далек от единообразия и отнюдь не независим. С математической точки зрения, это эргодично, но даже не смешивающе. Он не пройдёт большинство основных тестов с участием LLN и CLT. - person user334639; 09.08.2019
comment
Корреляция с 355 больше, чем с 710, потому что это непрерывная доля числа Пи: en.wikipedia .org / wiki / @spencernelson - person karpada; 26.04.2020

Нет, но вот простой генератор псевдослучайных чисел, реализация Умножение с переносом Я адаптировал из Википедии (с тех пор удален):

var m_w = 123456789;
var m_z = 987654321;
var mask = 0xffffffff;

// Takes any integer
function seed(i) {
    m_w = (123456789 + i) & mask;
    m_z = (987654321 - i) & mask;
}

// Returns number between 0 (inclusive) and 1.0 (exclusive),
// just like Math.random().
function random()
{
    m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
    m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;
    var result = ((m_z << 16) + (m_w & 65535)) >>> 0;
    result /= 4294967296;
    return result;
}

РЕДАКТИРОВАТЬ: исправлена ​​начальная функция путем сброса m_z
EDIT2: исправлены серьезные недостатки реализации

person Antti Kissaniemi    schedule 10.10.2013
comment
Кто-нибудь тестировал эту функцию на случайность? - person Justin; 01.01.2014
comment
Это генератор случайных чисел умножения с переносом (MWC) с красивым долгий период. По материалам генераторов случайных чисел википедии - person Michael_Scharf; 21.07.2014
comment
Функция seed не сбрасывает генератор случайных чисел, потому что переменная mz_z изменяется при вызове random(). Поэтому установите mz_z = 987654321 (или любое другое значение) в seed - person Michael_Scharf; 21.07.2014
comment
Когда я использую его со своим генератором случайных цветов (HSL), он генерирует только зеленый и голубой цвета. Исходный генератор случайных чисел генерирует все цвета. Значит, это не то же самое или не работает. - person Tomas Kubes; 07.12.2014
comment
@Michael_Scharf 1) Изменение начального числа m_w, а не m_z. 2) И m_w, и m_z изменяются НА ОСНОВЕ своих предыдущих значений, поэтому результат изменяется. - person ESL; 17.09.2015
comment
Когда я использовал этот код, я не получил хорошо распределенных результатов. Независимо от начального числа, выходная последовательность была очень похожей. Это сделало его бесполезным для моей игры. - person Martin Omander; 20.02.2017
comment
@Justin, это на несколько лет слишком поздно, но с несколькими семенами при миллионах прогонов он почти идеально распределен. Разница в распределении была около 3.469446951953614e-18, т. Е. чрезвычайно хорошо распределена. Это сценарий, который я использовал для тестирования распространения. Просто нужен объект с next() методом. - person Qix - MONICA WAS MISTREATED; 12.06.2017
comment
Изменение начального числа не приводит к совершенно другой последовательности случайных чисел, существует некая базовая закономерность. Это XOR двух битовых графиков с начальным числом 1 и 55. Белые пиксели имеют ‹0,5, черные пиксели составляют ›0,5. Выполнение XOR двух выходов с разными начальными числами показывает смещение в сторону ›0,5. - person bryc; 01.12.2017
comment
Я внес правку, чтобы устранить некоторые серьезные проблемы в коде. Он не был правильно посеян, и обходной путь для отрицательных чисел вызывал предвзятость. Это очень похоже на алгоритм MWC1616, который имеет плохую репутацию Что касается качества. - person bryc; 10.12.2018

Алгоритм Антти Сюкари красивый и короткий. Сначала я сделал вариант, который заменил Math.random в JavaScript при вызове Math.seed(s), но затем Джейсон прокомментировал, что было бы лучше вернуть функцию:

Math.seed = function(s) {
    return function() {
        s = Math.sin(s) * 10000; return s - Math.floor(s);
    };
};

// usage:
var random1 = Math.seed(42);
var random2 = Math.seed(random1());
Math.random = Math.seed(random2());

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

person Community    schedule 25.04.2014
comment
Если вы вернете функцию вместо установки Math.random, это позволит вам иметь несколько независимых генераторов, верно? - person Jason Goemaat; 29.05.2014
comment
Не забудьте увидеть выше комментарии о распределении случайности, если это важно для вас: stackoverflow.com/questions/521295/ - person jocull; 27.06.2016
comment
Как можно повторить порожденные этим случайности? Он каждый раз дает новые цифры - person SMUsamaShah; 25.12.2016
comment
каждый раз, когда вы делаете Math.seed(42);, функция сбрасывается, поэтому если вы сделаете var random = Math.seed(42); random(); random();, вы получите 0.70..., а затем 0.38.... Если вы сбросите его, позвонив var random = Math.seed(42); еще раз, то в следующий раз, когда вы позвоните random(), вы снова получите 0.70..., а в следующий раз снова получите 0.38.... - person WOUNDEDStevenJones; 07.12.2017
comment
Пожалуйста, не используйте это. Найдите время, чтобы вместо этого использовать локальную переменную с именем random вместо перезаписи встроенной функции javascript. Перезапись Math.random может привести к тому, что компилятор JIST не будет оптимизировать весь ваш код. - person Jack Giffin; 13.04.2018
comment
Предупреждение не работает для s = 0. Должен выдавать ошибку при построении. - person Rok Burgar; 07.03.2019

Пожалуйста, посмотрите работы Пьера Л'Экуйера, относящиеся к концу 1980-х - началу 1990-х годов. Есть и другие. Самостоятельное создание (псевдо) генератора случайных чисел, если вы не являетесь экспертом, довольно опасно, потому что высока вероятность того, что либо результаты не будут статистически случайными, либо будут иметь небольшой период. Пьер (и другие) собрали несколько хороших (псевдо) генераторов случайных чисел, которые легко реализовать. Я использую один из его генераторов LFSR.

https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf

Фил Трой

person user2383235    schedule 04.03.2017
comment
Отличный ответ, но не связанный с javascript :) - person Nikolay Fominyh; 04.03.2017
comment
Код для реализации работы профессора Л'Экуйера общедоступен для java и может быть легко переведен большинством программистов на Javascript. - person user2383235; 06.03.2017

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

Math.seed = function(s) {
    var mask = 0xffffffff;
    var m_w  = (123456789 + s) & mask;
    var m_z  = (987654321 - s) & mask;

    return function() {
      m_z = (36969 * (m_z & 65535) + (m_z >>> 16)) & mask;
      m_w = (18000 * (m_w & 65535) + (m_w >>> 16)) & mask;

      var result = ((m_z << 16) + (m_w & 65535)) >>> 0;
      result /= 4294967296;
      return result;
    }
}

var myRandomFunction = Math.seed(1234);
var randomNumber = myRandomFunction();
person user3158327    schedule 04.04.2015
comment
Это дает очень похожие результаты в начале последовательности с разными начальными числами. Например, Math.seed(0)() возвращает 0.2322845458984375, а Math.seed(1)() возвращает 0.23228873685002327. Кажется, помогает изменение m_w и m_z в зависимости от семени. var m_w = 987654321 + s; var m_z = 123456789 - s; дает хорошее распределение первых значений с разными начальными числами. - person undefined; 18.04.2016
comment
@undefined описанная вами проблема исправлена ​​на момент последнего редактирования, это была ошибка в реализации MWC. - person bryc; 22.02.2019
comment
Сейчас работает нормально, по состоянию на январь 2020 года. Посадите 0, получите 0,7322976540308446. Начальное значение с 1, 0,16818441334180534, с 2: 0,6040864314418286, с 3: 0,03998844954185188. Спасибо вам обоим! - person Eureka; 02.01.2020

Написать собственный генератор псевдослучайных чисел довольно просто.

Предложение Дэйва Скотеза полезно, но, как указывали другие, оно не совсем равномерно распределено.

Однако это не из-за целочисленных аргументов греха. Это просто из-за диапазона греха, который представляет собой одномерную проекцию круга. Если бы вместо этого вы взяли угол круга, он был бы однородным.

Поэтому вместо sin (x) используйте arg (exp (i * x)) / (2 * PI).

Если вам не нравится линейный порядок, немного смешайте его с xor. Фактический фактор тоже не имеет большого значения.

Для генерации n псевдослучайных чисел можно использовать код:

function psora(k, n) {
  var r = Math.PI * (k ^ n)
  return r - Math.floor(r)
}
n = 42; for(k = 0; k < n; k++) console.log(psora(k, n))

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

person Lajos Bodrogi    schedule 26.02.2017
comment
Я не эксперт, но последовательные начальные числа следуют постоянному шаблону. Цветные пиксели ›= 0,5. Я предполагаю, что это просто повторение радиуса снова и снова? - person bryc; 06.03.2017

Многие люди, которым в наши дни нужен генератор случайных чисел с возможностью заполнения в Javascript, используют модуль seedrandom Дэвида Бау.

person Martin Omander    schedule 07.12.2017

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

ran.core.seed(0)
myDist = new ran.Dist.Uniform(0, 1)
samples = myDist.sample(1000)
person Ulf Aslak    schedule 10.04.2019

Вот принятая версия хэша Jenkins, заимствованная из здесь

export function createDeterministicRandom(): () => number {
  let seed = 0x2F6E2B1;
  return function() {
    // Robert Jenkins’ 32 bit integer hash function
    seed = ((seed + 0x7ED55D16) + (seed << 12))  & 0xFFFFFFFF;
    seed = ((seed ^ 0xC761C23C) ^ (seed >>> 19)) & 0xFFFFFFFF;
    seed = ((seed + 0x165667B1) + (seed << 5))   & 0xFFFFFFFF;
    seed = ((seed + 0xD3A2646C) ^ (seed << 9))   & 0xFFFFFFFF;
    seed = ((seed + 0xFD7046C5) + (seed << 3))   & 0xFFFFFFFF;
    seed = ((seed ^ 0xB55A4F09) ^ (seed >>> 16)) & 0xFFFFFFFF;
    return (seed & 0xFFFFFFF) / 0x10000000;
  };
}

Вы можете использовать это так:

const deterministicRandom = createDeterministicRandom()
deterministicRandom()
// => 0.9872818551957607

deterministicRandom()
// => 0.34880331158638
person Kirill Groshkov    schedule 19.01.2021

Большинство ответов здесь дают необъективные результаты. Итак, вот протестированная функция, основанная на библиотеке seedrandom из github:

!function(f,a,c){var s,l=256,p="random",d=c.pow(l,6),g=c.pow(2,52),y=2*g,h=l-1;function n(n,t,r){function e(){for(var n=u.g(6),t=d,r=0;n<g;)n=(n+r)*l,t*=l,r=u.g(1);for(;y<=n;)n/=2,t/=2,r>>>=1;return(n+r)/t}var o=[],i=j(function n(t,r){var e,o=[],i=typeof t;if(r&&"object"==i)for(e in t)try{o.push(n(t[e],r-1))}catch(n){}return o.length?o:"string"==i?t:t+"\0"}((t=1==t?{entropy:!0}:t||{}).entropy?[n,S(a)]:null==n?function(){try{var n;return s&&(n=s.randomBytes)?n=n(l):(n=new Uint8Array(l),(f.crypto||f.msCrypto).getRandomValues(n)),S(n)}catch(n){var t=f.navigator,r=t&&t.plugins;return[+new Date,f,r,f.screen,S(a)]}}():n,3),o),u=new m(o);return e.int32=function(){return 0|u.g(4)},e.quick=function(){return u.g(4)/4294967296},e.double=e,j(S(u.S),a),(t.pass||r||function(n,t,r,e){return e&&(e.S&&v(e,u),n.state=function(){return v(u,{})}),r?(c[p]=n,t):n})(e,i,"global"in t?t.global:this==c,t.state)}function m(n){var t,r=n.length,u=this,e=0,o=u.i=u.j=0,i=u.S=[];for(r||(n=[r++]);e<l;)i[e]=e++;for(e=0;e<l;e++)i[e]=i[o=h&o+n[e%r]+(t=i[e])],i[o]=t;(u.g=function(n){for(var t,r=0,e=u.i,o=u.j,i=u.S;n--;)t=i[e=h&e+1],r=r*l+i[h&(i[e]=i[o=h&o+t])+(i[o]=t)];return u.i=e,u.j=o,r})(l)}function v(n,t){return t.i=n.i,t.j=n.j,t.S=n.S.slice(),t}function j(n,t){for(var r,e=n+"",o=0;o<e.length;)t[h&o]=h&(r^=19*t[h&o])+e.charCodeAt(o++);return S(t)}function S(n){return String.fromCharCode.apply(0,n)}if(j(c.random(),a),"object"==typeof module&&module.exports){module.exports=n;try{s=require("crypto")}catch(n){}}else"function"==typeof define&&define.amd?define(function(){return n}):c["seed"+p]=n}("undefined"!=typeof self?self:this,[],Math);

function randIntWithSeed(seed, max=1) {
  /* returns a random number between [0,max] including zero and max
  seed can be either string or integer */
  return Math.round(new Math.seedrandom('seed' + seed)()) * max
}

проверить истинную случайность этого кода: https://es6console.com/kkjkgur2/

person penduDev    schedule 30.01.2021

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

Использовать :

seedRandom("k9]:2@", 15)

он вернет ваше начальное число, первый параметр - любое строковое значение; ваше семя. второй параметр - сколько цифр вернется.

     function seedRandom(inputSeed, lengthOfNumber){

           var output = "";
           var seed = inputSeed.toString();
           var newSeed = 0;
           var characterArray = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','y','x','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','U','R','S','T','U','V','W','X','Y','Z','!','@','#','$','%','^','&','*','(',')',' ','[','{',']','}','|',';',':',"'",',','<','.','>','/','?','`','~','-','_','=','+'];
           var longNum = "";
           var counter = 0;
           var accumulator = 0;

           for(var i = 0; i < seed.length; i++){
                var a = seed.length - (i+1);
                for(var x = 0; x < characterArray.length; x++){
                     var tempX = x.toString();
                     var lastDigit = tempX.charAt(tempX.length-1);
                     var xOutput = parseInt(lastDigit);
                     addToSeed(characterArray[x], xOutput, a, i); 
                }                  
           }

                function addToSeed(character, value, a, i){
                     if(seed.charAt(i) === character){newSeed = newSeed + value * Math.pow(10, a)}
                }
                newSeed = newSeed.toString();

                var copy = newSeed;
           for(var i=0; i<lengthOfNumber*9; i++){
                newSeed = newSeed + copy;
                var x = Math.sin(20982+(i)) * 10000;
                var y = Math.floor((x - Math.floor(x))*10);
                longNum = longNum + y.toString()
           }

           for(var i=0; i<lengthOfNumber; i++){
                output = output + longNum.charAt(accumulator);
                counter++;
                accumulator = accumulator + parseInt(newSeed.charAt(counter));
           }
           return(output)
      }
person Tyler Hudson    schedule 13.04.2018
comment
Полученные таким образом последовательности чисел не совсем соответствуют свойствам последовательностей случайных чисел. Сгенерируйте с его помощью 15 чисел, и результирующая строка почти всегда начинается с 7 для почти любого ключа, например. - person Gabriel; 25.06.2018

В PHP есть функция srand(seed), которая генерирует фиксированное случайное значение для определенного начального числа. Но в JS такой встроенной функции нет.

Однако мы можем написать простую и короткую функцию.

Шаг 1: выберите какое-нибудь семя (исправленное число).
var seed = 100;
Число должно быть положительным целым числом больше 1, дальнейшее объяснение см. в шаге 2.

Шаг 2. Выполните функцию Math.sin () для Seed, она даст значение sin < / em> этого числа. Сохраните это значение в переменной x.

var x; 
x = Math.sin(seed); // Will Return Fractional Value between -1 & 1 (ex. 0.4059..)

Метод sin () возвращает дробное значение от -1 до 1.
И нам не нужно отрицательное значение, поэтому на первом шаге выберите число больше 1.

Шаг 3. Возвращаемое значение - это дробное значение от -1 до 1.
Итак, умножьте это значение на 10, чтобы оно больше 1.

x = x * 10; // 10 for Single Digit Number

Шаг 4. Умножьте значение на 10, чтобы получить дополнительные цифры.

x = x * 10; // Will Give value between 10 and 99 OR
x = x * 100; // Will Give value between 100 and 999

Умножьте в соответствии с требованиями цифр.

Результат будет в десятичном формате.

Шаг 5. Удалите значение после десятичной точки с помощью метода Math's Round (Math.round ()).

x = Math.round(x); // This will give Integer Value.

Шаг 6. Превратите отрицательные значения в положительные (если есть) с помощью метода Math.abs

x = Math.abs(x); // Convert Negative Values into Positive(if any)

Конец объяснения.

Окончательный код

var seed = 111; // Any Number greater than 1
var digit = 10 // 1 => single digit, 10 => 2 Digits, 100 => 3 Digits and so. (Multiple of 10) 

var x; // Initialize the Value to store the result
x = Math.sin(seed); // Perform Mathematical Sin Method on Seed.
x = x * 10; // Convert that number into integer
x = x * digit; // Number of Digits to be included
x = Math.round(x); // Remove Decimals
x = Math.abs(x); // Convert Negative Number into Positive

Чистый и оптимизированный функциональный код

function random_seed(seed, digit = 1) {
    var x = Math.abs(Math.round(Math.sin(seed++) * 10 * digit));
    return x;
}

Затем вызовите эту функцию, используя
random_seed(any_number, number_of_digits)
any_number должно быть больше 1.
number_of_digits - необязательный параметр и если ничего не прошло, вернется 1 цифра.

random_seed(555); // 1 Digit
random_seed(234, 1); // 1 Digit
random_seed(7895656, 1000); // 4 Digit
person manasGain    schedule 29.01.2021
comment
Эта функция смещена, поскольку abs (Math.sin (random_number)) сама по себе является функцией, смещенной вокруг точки, в которой наклон функции sin равен нулю. Вот код, который можно запустить в консоли js для эксперимента: (pastebin.com/kJyHaQYY) - person penduDev; 30.01.2021

Простой подход для фиксированных семян:

function fixedrandom(p){
    const seed = 43758.5453123;
    return (Math.abs(Math.sin(p)) * seed)%1;
}
person Carlos Oliveira    schedule 30.04.2018

Для числа от 0 до 100.

Number.parseInt(Math.floor(Math.random() * 100))
person Lord Elrond    schedule 10.04.2018
comment
Вопрос заключается в заполнении Math.random таким образом, чтобы всякий раз, когда Math.random был заполнен одним и тем же семенем, он давал одну и ту же последовательную серию случайных чисел. Этот вопрос, по сути, не касается фактического использования / демонстрации Math.random. - person Jack Giffin; 13.04.2018