Генерация случайных чисел в JavaScript: уникальные 500 целых чисел в пространстве 10 ^ 6: столкновения

1

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

Я генерирую только 500 уникальных случайных чисел из очень большого пространства, 10 ^ 6.

И все же, если вы продолжите нажимать кнопку, вы будете иногда видеть 499 или 498 уникальных из 500. Это случается не очень часто, но это происходит, возможно, при каждом 10-м или 15-м щелчке. Это почему? Мое пространство составляет 1 миллион. Я не ожидаю столкновения в выборке 500 с частотой каждого 10-го или 20-го клика.

Чтобы проверить, продолжайте нажимать кнопку и смотреть консоль.

function run() {
  var nums = new Set();

  for (var i = 0; i < 500; i++) {
    nums.add(randomInteger10to6th());
  }

  console.clear();
  console.log('Random 10^6 Unique Integer set: ' + nums.size);
}

function randomInteger10to6th() {
   return Math.round(Math.random() * Math.pow(10,6))
}
<button id="run" onclick="run();">Run 500 Random Integers, Space: 10^6</button>
  • 2
    dilbert.com/strip/2001-10-25
  • 8
    Вы слышали о парадоксе дня рождения? 500 случайных чисел среди 1000000 - это примерно 10% -ная вероятность столкновения (согласно некоторому случайному калькулятору, который я гуглил для парадокса дня рождения)
Показать ещё 2 комментария
Теги:
random

4 ответа

9
Лучший ответ

Вероятность того, что вы увидите все уникальные числа, когда выберете 500 случайных чисел из 1-1e6, может быть рассчитана следующим образом: 1e6/1e6 * 999,999/1e6 * 999,998/1e6 *... * 999,501/1e6

Это выходит около 0,88

Это означает, что более чем в 10% случаев в вашем списке из 500 случайностей будет хотя бы один дубликат.

Вы можете проверить это поведение в приведенном ниже фрагменте для 100 экспериментов:

function run() {
  var nums = new Set();
  for (var i = 0; i < 500; i++) {
    nums.add(randomInteger10to6th());
  }
  return nums;
}

function randomInteger10to6th() {
  return Math.round(Math.random() * Math.pow(10, 6))
}

// perform 100 experiments and see how many have duplicates
var uniques = 0, collisions = 0;
for (var i = 0; i < 100; i++) {
  var nums = run();
  if (nums.size === 500) uniques++;
  else collisions++;
}

console.log('Runs that generated unique numbers', uniques);
console.log('Runs that resulted in collisions', collisions);
1

Поскольку вы генерируете сравнительно небольшое количество случайных чисел из большой выборки, вы сможете восстановить новое число при столкновении. Добавление случайных чисел до 500 приведет к нескольким дополнительным вызовам в генератор случайных чисел, но это гарантирует 500 уникальных номеров:

function run() {
    var nums = new Set();
  
    while (nums.size < 500){
       nums.add(randomInteger10to6th());
     }
  
    console.clear();
    console.log('Random 10^6 Unique Integer set: ' + nums.size);
  }
  
function randomInteger10to6th() {
   return Math.round(Math.random() * Math.pow(10,6))
}
<button id="run" onclick="run();">Run 500 Random Integers, Space: 10^6</button>
  • 0
    было бы (намного) быстрее взять 650 и сохранить первые 500 в наборе, не сравнивая, и ваши шансы на неудачу астрономические, и их можно обойти дешево.
  • 1
    Почему дубликат чека? Разве это не смысл Сета в том, что он не допускает обман?
Показать ещё 3 комментария
1

"Случайный" означает только это: он случайный. Каждое значение в диапазоне имеет одинаковую вероятность быть выбранным, независимо от того, что было выбрано ранее. Таким образом, даже если он выбрал число 5, например, у него все еще остается тот же шанс выбрать 5 раз, как и при выборе любого другого числа. Вы не должны ожидать случайных чисел, чтобы избежать дубликатов - если бы они сделали, они не были бы случайными :)

  • 1
    Это не ответ на вопрос.
  • 0
    Это хороший момент, спасибо. Но мне нужен RNG, чтобы обеспечить достаточную уникальность для меня.
Показать ещё 8 комментариев
0

Вы также можете использовать простые массивы вместо Set(). Сначала создайте массив из 500 индексов, затем заполните его.

function run() {
      /* first create an array with 500 slots, then fill it with 
         undefined ( fill() without args is undefined ). For the 
         last step map through 500 slots of undefined and overwrite 
         it with a random number */
      return new Array(500).fill().map(x => Math.round(Math.random() * Math.pow(10,6)))
}

console.log(run().length)
<button id="run" onclick="run();">Run 500 Random Integers, Space: 10^6</button>

И если вы хотите удалить дубликаты, вы можете просто отфильтровать их пересчитать.

function run() {
  // same like the upper function but with a duplicates filter
  let arrayWithNumbers = new Array(500).fill().map(x => Math.round(Math.random() * Math.pow(10,6))).filter((item, idx, self) => self.indexOf(item) == idx)
  // run again if array less than 500
  if(arrayWithNumbers.length !== 500) { 
   run() 
  }

  return arrayWithNumbers
}

console.log(run().length)
<button id="run" onclick="run();">Run 500 Random Integers, Space: 10^6</button>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню