БлогNot. Плохой генератор случайных чисел в Javascript

Плохой генератор случайных чисел в 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 с расчётом
скриншот файла Excel с расчётом

P.S. Перспективно выглядит применение window.crypto.getRandomValues, но особой выгоды я тоже не заметил.

Вот что вышло для равномерно распределённых на интервале [0;1[ случайных чисел на серии из миллиона испытаний стандартным генератором Math.random и нашим Random.random.

Серии getRandomValues по 100 чисел
Серии 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>

теги: javascript random числа статистика excel

09.10.2018, 11:41; рейтинг: 1162