Плохой генератор случайных чисел в Javascript
Не устраивает качество стандартного Math.random()
, и, похоже, не только в "Хромом". Написать навскидку генератор лучше тоже не вышло, заархивирую его код сюда.
Мы генерируем по n = 10000
равномерно распределённых на интервале [0;1[
псевдослучайных чисел стандартным генератором и нашим, после чего выводим числа в 2 столбца в браузер.
В скопированных из браузера столбцах можно заменить символы точки запятыми (в Excel целая часть числа отделяется от дробной запятой, если установлена русская локаль) и вставить числа в столбцы A
и B
приложенного файла Excel, где подсчитаны выборочные и теоретические значения математического ожидания и дисперсии.
Видно, что наш генератор оказывается, увы, не лучше стандартного.
Вот код без обрамления HTML:
<pre> <div id="resRandom"></div> </pre> <script> let m_w = 123456789; let m_z = 987654321; let mask = 0xffffffff; function seed (i) { //Инициализация числом i m_w = i; m_z = i; //??? } function random() { //Случайное число от 0 (включая) до 1 (не включая) 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) & mask; result /= 4294967296; return result + 0.5; } var a = [], a_my = []; var n = 1000000; var d = new Date(); seed (d.getTime()); let str = ''; for (var i=0; i < n; i++) { a[i] = Math.random().toString().split('.').join(','); a_my[i] = random().toString().split('.').join(','); str += a[i] + "\t" + a_my[i] + "\n"; } document.getElementById('resRandom').innerHTML = str; </script>
Подготовить данные для построения частотной диаграммы распределения чисел по интервалам, набитым с шагом step
можно бы было примерно так:
var step = 0.001; //шаг для построения частотной диаграммы var m =Math.floor (1/step); var a_c = new Array (m); var a_my_c = new Array (m); for (var i=0; i<m; i++) a_c[i] = a_my_c[i] = 0; for (var i=0; i<n; i++) { var index = Math.floor (a[i]/step); a_c[index]++; index = Math.floor (a_my[i]/step); a_my_c[index]++; } document.getElementById('resRandom').innerHTML = ''; for (var i=0; i<m; i++) document.getElementById('resRandom').innerHTML += a_c[i] + "\t" + a_my_c[i] + "\n";
Изменить распределение случайных чисел не нормальное можно так:
function randomNormal() { return Math.cos(2 * Math.PI * Math.random()) * Math.sqrt(-2 * Math.log(Math.random())); } var a = []; for (var i=0; i < 1000; i++) a[i] = randomNormal() / 2 + 1;
Файл .zip с архивом .xlsx этого расчёта (325 Кб)
скриншот файла Excel с расчётом
P.S. Перспективно выглядит применение window.crypto.getRandomValues, но особой выгоды я тоже не заметил.
Вот что вышло для равномерно распределённых на интервале [0;1[ случайных чисел на серии из миллиона испытаний стандартным генератором Math.random
и нашим Random.random
.
Серии getRandomValues по 100 чисел
При этом на более коротких цепочках "обновления" последовательностей методом getRandomValues
(например, каждые 100 элементов)
наш генератор, кажется, работает лучше, а вот исходник.
<pre> <div id="resRandom"></div> </pre> <script> var Random = { arrSize: 100, cnt: 100, arrStor: [], random: function () { if (this.cnt == this.arrSize) { this.arrStor = new Uint32Array(this.arrSize); window.crypto.getRandomValues(this.arrStor); this.cnt = 0; } let d = this.arrStor[this.cnt] / (Math.pow(2,32) - 1); this.cnt++; return d; } }; let a = [], a_my = []; let n = 1000000; let str = ''; for (let i=0; i < n; i++) { a[i] = Math.random().toString().split('.').join(','); a_my[i] = Random.random().toString().split('.').join(','); str += a[i] + "\t" + a_my[i] + "\n"; } document.getElementById('resRandom').innerHTML = str; </script>
09.10.2018, 11:41 [2809 просмотров]