Генерация уникальных случайных чисел от 1 до 100 - PullRequest
79 голосов
/ 04 марта 2010

Как я могу сгенерировать 8, скажем, уникальных случайных чисел от 1 до 100, используя JavaScript?

Ответы [ 26 ]

1 голос
/ 04 марта 2010

Тот же алгоритм перестановки, что и у The Machine Charmer, но с прототипной реализацией. Лучше подходит для большого количества пиков. Использует js 1.7 деструктурирующее назначение , если доступно.

// swaps elements at index i and j in array this
// swapping is easy on js 1.7 (feature detection)
Array.prototype.swap = (function () {
    var i=0, j=1;
    try { [i,j]=[j,i]; }
    catch (e) {}
    if(i) {
        return function(i,j) {
            [this[i],this[j]] = [this[j],this[i]];
            return this;
        }
    } else {
        return function(i,j) {
            var temp = this[i];
            this[i] = this[j];
            this[j] = temp;
            return this;
        }
    }
})();


// shuffles array this
Array.prototype.shuffle = function() {
    for(var i=this.length; i>1; i--) {
        this.swap(i-1, Math.floor(i*Math.random()));
    }
    return this;
}

// returns n unique random numbers between min and max
function pick(n, min, max) {
    var a = [], i = max;
    while(i >= min) a.push(i--);
    return a.shuffle().slice(0,n);
}

pick(8,1,100);

Edit: Другое предложение, более подходящее для небольшого числа выборов, основано на ответе белугабоба. Чтобы гарантировать уникальность, мы удаляем выбранные числа из массива.

// removes n random elements from array this
// and returns them
Array.prototype.pick = function(n) {
    if(!n || !this.length) return [];
    var i = Math.floor(this.length*Math.random());
    return this.splice(i,1).concat(this.pick(n-1));
}

// returns n unique random numbers between min and max
function pick(n, min, max) {
    var a = [], i = max;
    while(i >= min) a.push(i--);
    return a.pick(n);
}

pick(8,1,100);
1 голос
/ 19 февраля 2014

Лучшим ранним ответом является ответ sje397. Вы получите как можно больше случайных чисел, как можно быстрее.

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

function selectKOutOfN(k, n) {
  if (k>n) throw "k>n";
  var selection = [];
  var sorted = [];
  for (var i = 0; i < k; i++) {
    var rand = Math.floor(Math.random()*(n - i));
    for (var j = 0; j < i; j++) {
      if (sorted[j]<=rand)
        rand++;
      else
        break;
    }
    selection.push(rand);
    sorted.splice(j, 0, rand);
  }
  return selection;
}

alert(selectKOutOfN(8, 100));
1 голос
/ 15 декабря 2015
var arr = []
while(arr.length < 8){
  var randomnumber=Math.ceil(Math.random()*100)
  if(arr.indexOf(randomnumber) === -1){arr.push(randomnumber)}  
}
document.write(arr);

короче, чем другие ответы, которые я видел

1 голос
/ 20 августа 2018

Еще один более простой подход - создать массив из 100 элементов с возрастающими числами и отсортировать его случайным образом. На самом деле это приводит к очень короткому и (на мой взгляд) простому фрагменту.

const numbers = [ ...Array(100).keys() ].map(num => num + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));
0 голосов
/ 24 августа 2018

Использование Set - ваш самый быстрый вариант. Вот общая функция для получения уникального случайного числа, которое использует генератор обратного вызова. Теперь это быстро и многоразово .

// Get a unique 'anything'
let unique = new Set()

function getUnique(generator) {
  let number = generator()
  while (!unique.add(number)) {
    number = generator()
  }
  return number;
}

// The generator.  Return anything, not just numbers.
const between_1_100 = () => 1 + Math.floor(Math.random() * 100)

// Test it
for (var i = 0; i < 8; i++) {
  const aNumber = getUnique(between_1_100)
}
// Dump the 'stored numbers'
console.log(Array.from(unique))
0 голосов
/ 04 июля 2018
getRandom (min, max) {
  return Math.floor(Math.random() * (max - min)) + min
}

getNRandom (min, max, n) {
  const numbers = []
  if (min > max) {
    return new Error('Max is gt min')
  }

  if (min === max) {
    return [min]
  }

  if ((max - min) >= n) {
    while (numbers.length < n) {
      let rand = this.getRandom(min, max + 1)
      if (numbers.indexOf(rand) === -1) {
        numbers.push(rand)
      }
    }
  }

  if ((max - min) < n) {
    for (let i = min; i <= max; i++) {
      numbers.push(i)
    }
  }
  return numbers
}
0 голосов
/ 04 марта 2010

Как насчет использования свойств объекта в качестве хеш-таблицы ? Таким образом, ваш лучший сценарий - рандомизировать только 8 раз. Это будет эффективно только в том случае, если вы хотите небольшую часть диапазона чисел. Это также намного меньше памяти, чем Фишер-Йейтс, потому что вам не нужно выделять место для массива.

var ht={}, i=rands=8;
while ( i>0 || keys(ht).length<rands) ht[Math.ceil(Math.random()*100)]=i--;
alert(keys(ht));

Затем я обнаружил, что Object.keys (obj) является функцией ECMAScript 5, поэтому вышеприведенное в значительной степени бесполезно для интернет-сетей прямо сейчас. Не бойтесь, потому что я сделал его совместимым с ECMAScript 3, добавив функцию ключей, подобную этой.

if (typeof keys == "undefined") 
{ 
  var keys = function(obj) 
  {
    props=[];
    for (k in ht) if (ht.hasOwnProperty(k)) props.push(k);
    return props;
  }
}
0 голосов
/ 07 сентября 2017

Вы также можете сделать это с одним вкладышем, как это:

[...((add, set) => add(set, add))((set, add) => set.size < 8 ? add(set.add(Math.floor(Math.random()*100) + 1), add) : set, new Set())]

0 голосов
/ 13 октября 2016

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

Эта sample функция работает лениво, давая вам 1 случайный элемент за итерацию до N элементов, которые вы запрашиваете.Это хорошо, потому что если вы просто хотите 3 пунктов из списка 1000 , вам не нужно сначала трогать все 1000 элементов.

// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
  let ys = xs.slice(0);
  let len = xs.length;
  while (n > 0 && len > 0) {
    let i = (Math.random() * len) >> 0;
    yield ys.splice(i,1)[0];
    n--; len--;
  }
}

// example inputs
let items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// get 3 random items
for (let i of sample(3) (items))
  console.log(i); // f g c

// partial application
const lotto = sample(3);
for (let i of lotto(numbers))
  console.log(i); // 3 8 7

// shuffle an array
const shuffle = xs => Array.from(sample (Infinity) (xs))
console.log(shuffle(items)) // [b c g f d e a]

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

Например, функция shuffle может захотеть изменить исходный массив ввода.Или вы можете выбрать один и тот же вход в разное время, обновляя вход каждый раз.

// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
  let len = xs.length;
  while (n > 0 && len > 0) {
    let i = (Math.random() * len) >> 0;
    yield xs.splice(i,1)[0];
    n--; len--;
  }
}

// deal :: [Card] -> [Card]
const deal = xs => Array.from(sample (2) (xs));

// setup a deck of cards (13 in this case)
// cards :: [Card]
let cards = 'A234567890JQK'.split('');

// deal 6 players 2 cards each
// players :: [[Card]]
let players = Array.from(Array(6), $=> deal(cards))

console.log(players);
// [K, J], [6, 0], [2, 8], [Q, 7], [5, 4], [9, A]

// `cards` has been mutated. only 1 card remains in the deck
console.log(cards);
// [3]

sample больше не является pure функцией из-за входной мутации массива, но в определенных обстоятельствах (продемонстрированных выше) это может иметь больше смысла.


Еще одна причина, по которой я выбрал генератор вместо функции, которая просто возвращает массив, заключается в том, что вы можете продолжить выборку до какого-то определенного условия.число из списка из 1 000 000 случайных чисел.

  • «Сколько мне нужно выбрать?» - вам не нужно указывать
  • "Должен ли я сначала найти все простые числа, а затем выбрать случайное простое число? " - Нет.

Поскольку мы работаем с генератором, эта задача тривиальна

const randomPrimeNumber = listOfNumbers => {
  for (let x of sample(Infinity) (listOfNumbers)) {
    if (isPrime(x))
      return x;
  }
  return NaN;
}

Это будет непрерывно производить выборку 1 случайного числа за раз, x, проверять, является ли оно простым, а затем возвращать x, если оно есть.Если список чисел исчерпан до того, как найдено простое число, возвращается NaN.


Примечание:

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

0 голосов
/ 21 сентября 2016

В этом решении используется хеш, который намного эффективнее O (1), чем проверка, находится ли он в массиве. Он также имеет дополнительные безопасные проверки. Надеюсь, это поможет.

function uniqueArray(minRange, maxRange, arrayLength) {
  var arrayLength = (arrayLength) ? arrayLength : 10
  var minRange = (minRange !== undefined) ? minRange : 1
  var maxRange = (maxRange !== undefined) ? maxRange : 100
  var numberOfItemsInArray = 0
  var hash = {}
  var array = []

  if ( arrayLength > (maxRange - minRange) ) throw new Error('Cannot generate unique array: Array length too high')

  while(numberOfItemsInArray < arrayLength){
    // var randomNumber = Math.floor(Math.random() * (maxRange - minRange + 1) + minRange)
    // following line used for performance benefits
    var randomNumber = (Math.random() * (maxRange - minRange + 1) + minRange) << 0

    if (!hash[randomNumber]) {
      hash[randomNumber] = true
      array.push(randomNumber)
      numberOfItemsInArray++
    }
  }
  return array
}
document.write(uniqueArray(1, 100, 8))
...