Генерация уникальных случайных чисел от 1 до 100


99

Как с помощью JavaScript сгенерировать уникальные случайные числа от 1 до 100?


19
Не совсем обман, так как это фокусируется на javascript.
— dotty 04

2
@dotty: ну, нет существенной разницы между тем, чтобы сделать это в Javascript и сделать это на любом другом языке, но я не буду голосовать за закрытие.
— Pointy

1
Я тоже не буду голосовать за закрытие. Это достаточно конкретно.
— Джош Стодола,


1
Есть еще один более чистый способ сделать это stackoverflow.com/questions/51898200/…
— Хуангизм

Ответы:


174

Например: чтобы сгенерировать 8 уникальных случайных чисел и сохранить их в массиве, вы можете просто сделать это:

var arr = [];
while(arr.length < 8){
    var r = Math.floor(Math.random() * 100) + 1;
    if(arr.indexOf(r) === -1) arr.push(r);
}
console.log(arr);


15
Фактический код намного лучше для таких вопросов, чем псевдокод;) (удалил мой ответ, который был псевдокодом ...)
— Роман Старков

3
O можно выбрать; используйте var randomnumber = Math.ceil (Math.random () * 100)
— Alsciende,

9
-1: этот алгоритм - наивный подход; это очень неэффективно.
— Frerich Raabe

40
Вот это да. Наивный кажется немного сильным. Возможно, это не лучшее решение, но оно простое, короткое, легко увидеть, что происходит, и работает в рамках приемлемых рабочих параметров для того, что необходимо выполнить. Переходим к следующей задаче. Совершенство - это здорово, но «готово» лучше, чем «идеально».
— adam0101 04

4
Есть вероятность, что функция вернет 0 в массиве. По этой ссылке: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… , Math.random () Returns a random number between 0 (inclusive) and 1 (exclusive). Если the Math.random()случайно возвращается 0, то Math.ceil(0)также будет 0, хотя вероятность невысока.
— Цянь Чен

43
  1. Заполните массив числами от 1 до 100.
  2. Перемешайте .
  3. Возьмите первые 8 элементов получившегося массива.

8
конечно, более эффективно настроить код так, чтобы он выполнял только первые 8 перетасовок? (а затем возьмем последние 8 элементов полу-перемешанного массива)
— второй

1
Я тоже всегда так делаю. Так что, если мне нужно десять случайных строк из файла с кучей строк в нем, я это сделаю randlines file | head -10.
— tchrist

1
Я думаю, что это правильный ответ, потому что он поддерживает вероятностное искажение, которого нет в принятом ответе,
— Роберто Томас

2
Что если N = 10 ^ 12? Не очень эффективно.
— shinzou

2
@shinzou в реальном мире вы не будете сортировать 10 ^ 12 чисел с помощью JavaScript. Тиривальный вопрос требует банального ответа. Я здесь не для того, чтобы решить проблему голода в мире. Я хорошо подготовлен для этого, но вопрос не в этом.
— ЯegDwight

14

Создать перестановку из 100 чисел, а затем выберите последовательно.

Используйте алгоритм Knuth Shuffle (он же Fisher-Yates shuffle) .

JavaScript:

  function fisherYates ( myArray,stop_count ) {
  var i = myArray.length;
  if ( i == 0 ) return false;
  int c = 0;
  while ( --i ) {
     var j = Math.floor( Math.random() * ( i + 1 ) );
     var tempi = myArray[i];
     var tempj = myArray[j];
     myArray[i] = tempj;
     myArray[j] = tempi;

     // Edited thanks to Frerich Raabe
     c++;
     if(c == stop_count)return;

   }
}

КОД, КОПИРОВАННЫЙ ПО ССЫЛКЕ.

ИЗМЕНИТЬ :

Улучшенный код:

function fisherYates(myArray,nb_picks)
{
    for (i = myArray.length-1; i > 1  ; i--)
    {
        var r = Math.floor(Math.random()*i);
        var t = myArray[i];
        myArray[i] = myArray[r];
        myArray[r] = t;
    }

    return myArray.slice(0,nb_picks);
}

Потенциальная проблема:

Предположим, у нас есть массив из 100 чисел {например, [1,2,3 ... 100]}, и мы перестаем менять местами после 8 перестановок; тогда в большинстве случаев массив будет выглядеть как {1,2,3,76,5,6,7,8, ... числа здесь будут перемешаны ... 10}.

Поскольку каждое число будет поменяться местами с вероятностью 1/100, так что, вероятно,. замены первых 8 номеров составляет 8/100, тогда как вероятность. из подмены других 92 это 92/100.

Но если мы запустим алгоритм для полного массива, то мы уверены (почти), что каждая запись поменяется местами.

В противном случае возникает вопрос: какие 8 чисел выбрать?


5
Этот подход правильный, но неоптимальный: вы можете прекратить перемешивание после восьми замен, поскольку вам нужно только восемь случайных чисел. Приведенный выше код меняет местами весь массив (в этом сценарии 100 элементов).
— Frerich Raabe

Код можно серьезно улучшить. Возвращаемые значения, побочные эффекты и использование функций - все это действительно размыто. Может быть, если вы напишете функцию, которая точно отвечает на исходную проблему, используя функцию fisherYates, она будет более ясной.
— Alsciende, 04

1
Ответ обновлен улучшенным кодом. Также @Frerich Raabe: упоминается проблема с остановкой после восьми свопов.
— Pratik Deoghare

Ваш алгоритм Фишера-Йейтса неверен. r должно зависеть от i. См. Мой ответ: stackoverflow.com/questions/2380019/…
— Alsciende

Упс извините за мою ужасную ошибку !! Ваша реализация классная. Понравилось. +1. Пожалуйста, дайте мне знать, если что-то еще не так. Спасибо.
— Pratik Deoghare 05

12

Современное решение JS с использованием Set (и средний случай O (n))

const nums = new Set();
while(nums.size !== 8) {
  nums.add(Math.floor(Math.random() * 100) + 1);
}

console.log([...nums]);


Почему это O (n)? Разве он не может зацикливаться на произвольное время?
— Энтони Визер

@AnthonyWieser Ты прав, худший случай. Я имел в виду средний случай, так как Set.add равен o (1)
— Алистер

Я думаю, что это может вернуть 0, как и мой ответ до того, как он был изменен для использованияMath.floor(Math.random()*100) + 1
— adam0101

Очень здорово открывать для себя SetJS! Однако не приведет ли это решение к ненужной генерации чисел до тех пор, пока одно из них не будет соответствовать требованию уникальности, особенно на последних итерациях, если 8 было ближе к 100? Таким образом, я думаю, что предпочитаю также элегантный ответ sortниже.
— Гилад Барнер,

10

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

Конкретно решить ваш вопрос с помощью Chance так же просто, как:

// One line!
var uniques = chance.unique(chance.natural, 8, {min: 1, max: 100});

// Print it out to the document for this snippet so we can see it in action
document.write(JSON.stringify(uniques));
<script src="http://chancejs.com/chance.min.js"></script>

Отказ от ответственности, как автор Chance, я немного предвзят;)


Голосуйте за, потому что я никогда раньше не видел фрагмент кода запуска
— surfmuggle

если я хочу создать код (8 случайных буквенно-цифровых строк) для купонов, который должен быть уникальным, как я могу сделать это с помощью Chance.js? примечание: купоны будут изготовлены по запросу, поэтому количество кодов будет неопределенным
— Оскар Юандината

@OscarYuandinata, это просто, просто сделайте. var codes = chance.unique(chance.string, 8)Если вам нужны коды, извлеченные из определенного пула символов, вы можете указать это следующим образом: chance.unique(chance.string, 8, {pool: "abcd1234"})где abcd1234 может быть любым символом, который вы хотите в пуле. См. Chancejs.com/#string
— Виктор Куинн

@VictorQuinn, извини, я не ясно выразился. Я имею в виду, что код купона будет представлять собой случайную буквенно-цифровую строку из 8 символов, а не массив из 8 случайных буквенно-цифровых строк. хахаха ..
— Оскар Юандината

О, @OscarYuandinata, это намного проще, хех, chance.string({ length: 8 })и если вы хотите, чтобы в этой строке отображались только определенные символы, chance.string({ pool: 'abcd1234', length: 8 })которая будет возвращать случайную строку из 8 символов из символов abcd1234, например, «2c2c44bc» или «331141cc»
— Виктор Куинн

8

Чтобы избежать долгих и ненадежных перетасовок, я бы сделал следующее ...

  1. Создайте массив, содержащий числа от 1 до 100 по порядку.
  2. Сгенерировать случайное число от 1 до 100
  3. Найдите номер этого индекса в массиве и сохраните его в результатах
  4. Удалите элемент из массива, сделав его на один короче
  5. Повторите действия с шага 2, но используйте 99 в качестве верхнего предела случайного числа.
  6. Повторите действия с шага 2, но используйте 98 в качестве верхнего предела случайного числа.
  7. Повторите с шага 2, но используйте 97 как верхний предел случайного числа.
  8. Повторите действия с шага 2, но используйте 96 в качестве верхнего предела случайного числа.
  9. Повторите с шага 2, но используйте 95 как верхний предел случайного числа.
  10. Повторите действия с шага 2, но используйте 94 в качестве верхнего предела случайного числа.
  11. Повторите с шага 2, но используйте 93 как верхний предел случайного числа.

Вуаля - никаких повторяющихся цифр.

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

Изменить: это, вероятно, соревновательная полоса во мне, но, увидев сообщение @Alsciende, я не мог удержаться от публикации кода, который я обещал.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>8 unique random number between 1 and 100</title>
<script type="text/javascript" language="Javascript">
    function pick(n, min, max){
        var values = [], i = max;
        while(i >= min) values.push(i--);
        var results = [];
        var maxIndex = max;
        for(i=1; i <= n; i++){
            maxIndex--;
            var index = Math.floor(maxIndex * Math.random());
            results.push(values[index]);
            values[index] = values[maxIndex];
        }
        return results;
    }
    function go(){
        var running = true;
        do{
            if(!confirm(pick(8, 1, 100).sort(function(a,b){return a - b;}))){
                running = false;
            }
        }while(running)
    }
</script>
</head>

<body>
    <h1>8 unique random number between 1 and 100</h1>
    <p><button onclick="go()">Click me</button> to start generating numbers.</p>
    <p>When the numbers appear, click OK to generate another set, or Cancel to stop.</p>
</body>


Но тогда ваше восьмое число будет случайным от 1 до 92, а не от 1 до 100. Если бы вам нужно было выбрать 90 чисел, ваше последнее число было бы выбрано только от 1 до 10, не так ли?
— adam0101 04

@ adam0101 Нет, потому что он удаляет числа по мере их выбора. Итак, на шаге 5 в его массиве всего 99 чисел. @belugabob Вы не более эффективны, чем Knuth Shuffle. Фактически, сварка, вероятно, дороже, чем перетасовка (что совершенно надежно)
— Alsciende

@ adam0101: он удаляет выбранный элемент из массива (см. шаг 4 выше), избегая, таким образом, двойного выбора любых элементов. Затем он использует нижнюю верхнюю границу для следующего случайного числа просто потому, что массив короче.
— Frerich Raabe

@Alsciende, да - думал, что есть способ сделать это более эффективно с помощью тасования, но не был полностью уверен. Чтобы избежать удаления элемента из массива, просто скопируйте последнюю запись из массива (при условии, что это не та, которую вы выбрали) в позицию, из которой вы выбрали.
— belugabob 04

1
Причина отсутствия уменьшения values.length заключается в том, что нет гарантии, что уменьшение длины массива не будет выполнено путем перераспределения памяти. Использование maxIndex имеет тот же эффект, просто игнорируя последние записи в массиве, поскольку они становятся неактуальными.
— belugabob 05

8

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

const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));


Это мой любимый ответ из всех. Не знаю, почему он получил только 6 голосов. Элегантно и с хорошей сложностью (при условии, sortхорошо реализовано, в чем я уверен).
— Гилад Барнер,

3

Я бы сделал это:

function randomInt(min, max) {
    return Math.round(min + Math.random()*(max-min));
}
var index = {}, numbers = [];
for (var i=0; i<8; ++i) {
    var number;
    do {
        number = randomInt(1, 100);
    } while (index.hasOwnProperty("_"+number));
    index["_"+number] = true;
    numbers.push(number);
}
delete index;

3

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

/* Creates an array of random integers between the range specified 
     len = length of the array you want to generate
     min = min value you require
     max = max value you require
     unique = whether you want unique or not (assume 'true' for this answer)
*/
    function _arrayRandom(len, min, max, unique) {
        var len = (len) ? len : 10,
                min = (min !== undefined) ? min : 1,
                max = (max !== undefined) ? max : 100,
                unique = (unique) ? unique : false,
                toReturn = [], tempObj = {}, i = 0;

        if(unique === true) {
            for(; i < len; i++) {
                var randomInt = Math.floor(Math.random() * ((max - min) + min));
                if(tempObj['key_'+ randomInt] === undefined) {
                    tempObj['key_'+ randomInt] = randomInt;
                    toReturn.push(randomInt);
                } else {
                    i--;
                }
            }
        } else {
            for(; i < len; i++) {
                toReturn.push(Math.floor(Math.random() * ((max - min) + min)));
            }
        }

        return toReturn;
    }

Здесь tempObj - очень полезный объект, поскольку каждое сгенерированное случайное число будет напрямую проверять в этом tempObj, если этот ключ уже существует, если нет, то мы уменьшаем i на единицу, поскольку нам нужен 1 дополнительный прогон, так как текущее случайное число уже существует. .

В вашем случае запустите следующее

_arrayRandom(8, 1, 100, true);

Вот и все.


что произойдет, если я захочу добавить 0? строка min = (min) ? min : 1,всегда возвращает 1. (поэтому 0 никогда не будет выбран)
— TBE

Очень хороший момент. :). Спасибо, я внес соответствующие изменения. Теперь он будет возвращаться, даже если вы передадите 0.
— kaizer1v

2

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

Я не очень хорошо знаю Javascript, но считаю, что быстро создать массив из 100 нулей легко. Затем в течение 8 раундов вы меняете местами n-й элемент массива (n начиная с 0) на случайно выбранный элемент от n + 1 до 99. Конечно, любые еще не заполненные элементы означают, что элемент действительно был исходный индекс плюс 1, так что его легко учесть. Когда вы закончите с 8 раундами, первые 8 элементов вашего массива будут иметь ваши 8 перемешанных чисел.


2
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

Тот же алгоритм перестановки, что и 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);

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

// 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);

Хорошая рекурсивная реализация - в своем ответе я опубликовал альтернативу, в которой не используется сращивание, поскольку я считаю, что это предотвратимое снижение производительности (не то чтобы у OP были какие-либо проблемы с производительностью)
— belugabob

Ваше решение умно, но я не буду использовать его в моем массиве # выбрать метод , потому что я не хочу этого , чтобы его элементы перемешиваются вокруг , когда я вернуть его.
— Alsciende 05

Какой массив вы не хотите перемешивать, исходный массив 1-100 или результаты? Первый не имеет значения, так как это рабочий массив, а второй, по природе кода, в любом случае будет выводиться в случайном порядке. Не совсем уверен, что понимаю ваши причины.
— belugabob 05

Оригинальный. Я реализовал общий метод выбора Array #, который считаю полезным. Эта функция не знает , если это является рабочим массивом или нет. Для того, чтобы быть универсальным, он не изменяет это больше , чем необходимо.
— Alsciende 05

Но он все же меняет его, пусть даже немного, что невозможно при использовании этой техники.
— belugabob 05

1

для массивов с такими отверстиями [,2,,4,,6,7,,] потому что моя проблема заключалась в том, чтобы заполнить эти дыры. Поэтому я изменил его по своему усмотрению :)

следующее модифицированное решение сработало для меня :)

var arr = [,2,,4,,6,7,,]; //example
while(arr.length < 9){
  var randomnumber=Math.floor(Math.random()*9+1);
  var found=false;
  for(var i=0;i<arr.length;i++){
    if(arr[i]==randomnumber){found=true;break;}
  }

  if(!found)
    for(k=0;k<9;k++)
    {if(!arr[k]) //if it's empty  !!MODIFICATION
      {arr[k]=randomnumber; break;}}
}

alert(arr); //outputs on the screen

1

Лучший предыдущий ответ - это ответ 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

Вот моя версия ES6, которую я сколотил. Я уверен, что его можно немного закрепить.

function randomArray(i, min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  
  let arr = Array.from({length: i}, () => Math.floor(Math.random()* (max - min)) + min);
  
  return arr.sort();
 }
 
 let uniqueItems = [...new Set(randomArray(8, 0, 100))]
 console.log(uniqueItems);


0

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

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
var bombout=0;
var checkArr=[];
var arr=[];
while(arr.length < 8 && bombout<100){
  bombout++;
  var randomNumber=Math.ceil(Math.random()*100);
  if(typeof checkArr[randomNumber] == "undefined"){
    checkArr[randomNumber]=1;
    arr.push(randomNumber);
  }
}​

// untested - hence bombout

0

если вам нужно больше уникальности, вы должны создать массив (1..100).

var arr=[];
function generateRandoms(){
for(var i=1;i<=100;i++) arr.push(i);
}
function extractUniqueRandom()
{
   if (arr.length==0) generateRandoms();
   var randIndex=Math.floor(arr.length*Math.random());
   var result=arr[randIndex];
   arr.splice(randIndex,1);
   return result;

}
function extractUniqueRandomArray(n)
{
   var resultArr=[];
   for(var i=0;i<n;i++) resultArr.push(extractUniqueRandom());
   return resultArr;
}

приведенный выше код работает быстрее:
extractUniqueRandomArray (50) => [2, 79, 38, 59, 63, 42, 52, 22, 78, 50, 39, 77, 1, 88, 40, 23, 48, 84, 91, 49, 4, 54, 93, 36, 100, 82, 62, 41, 89, 12, 24, 31, 86, 92, 64, 75, 70, 61, 67, 98, 76, 80, 56, 90, 83, 44, 43, 47, 7, 53]


0

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

var arr = []
while(arr.length < 8){
  var randomnumber=Math.ceil(Math.random()*100)
  var found=false;
    if(arr.indexOf(randomnumber) > -1){found=true;}
  if(!found)arr[arr.length]=randomnumber;
}

Более старая версия Javascript все еще может использовать версию сверху

PS: Пытался предложить обновление вики, но оно было отклонено. Я все еще думаю, что это может быть полезно другим.


0

Это мое личное решение:

<script>

var i, k;
var numbers = new Array();
k = Math.floor((Math.random()*8));
numbers[0]=k;
    for (var j=1;j<8;j++){
        k = Math.floor((Math.random()*8));
i=0;
while (i < numbers.length){
if (numbers[i] == k){
    k = Math.floor((Math.random()*8));
    i=0;
}else {i++;}
}
numbers[j]=k;
    }
    for (var j=0;j<8;j++){
alert (numbers[j]);
    }
</script>

Он случайным образом генерирует 8 уникальных значений массива (от 0 до 7), а затем отображает их с помощью окна предупреждения.


0
function getUniqueRandomNos() {
    var indexedArrayOfRandomNo = [];
    for (var i = 0; i < 100; i++) {
        var randNo = Math.random();
        indexedArrayOfRandomNo.push([i, randNo]);
    }
    indexedArrayOfRandomNo.sort(function (arr1, arr2) {
        return arr1[1] - arr2[1]
    });
    var uniqueRandNoArray = [];
    for (i = 0; i < 8; i++) {
        uniqueRandNoArray.push(indexedArrayOfRandomNo[i][0]);
    }
    return uniqueRandNoArray;
}

Я думаю, что этот метод отличается от методов, приведенных в большинстве ответов, поэтому я подумал, что могу добавить здесь ответ (хотя вопрос был задан 4 года назад).

Мы генерируем 100 случайных чисел и помечаем каждое из них числами от 1 до 100. Затем мы сортируем эти помеченные случайные числа, и теги перемешиваются случайным образом. В качестве альтернативы, при необходимости в этом вопросе, можно было бы просто найти 8 первых помеченных случайных чисел. Найти 8 лучших элементов дешевле, чем сортировать весь массив.

Здесь следует отметить, что алгоритм сортировки влияет на этот алгоритм. Если используемый алгоритм сортировки стабилен, есть небольшое смещение в пользу меньших чисел. В идеале мы бы хотели, чтобы алгоритм сортировки был нестабильным и даже не был склонен к стабильности (или нестабильности), чтобы давать ответ с идеально однородным распределением вероятностей.


0

Это может обрабатывать генерацию УНИКАЛЬНЫХ случайных чисел до 20 цифр.

JS

 var generatedNumbers = [];

    function generateRandomNumber(precision) { // input --> number precision in integer 
        if (precision <= 20) {
            var randomNum = Math.round(Math.random().toFixed(precision) * Math.pow(10, precision));
            if (generatedNumbers.indexOf(randomNum) > -1) {
                if (generatedNumbers.length == Math.pow(10, precision))
                    return "Generated all values with this precision";
                    return generateRandomNumber(precision);
            } else {
                generatedNumbers.push(randomNum);
                return randomNum;
            }
        } else
           return "Number Precision shoould not exceed 20";
    }
    generateRandomNumber(1);

введите описание изображения здесь

jsFiddle


0

Это решение использует хэш, который намного эффективнее 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))

0

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

Эта 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больше не является чистой функцией из-за мутации входных данных массива, но в определенных обстоятельствах (показанных выше) это может иметь больше смысла.


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

Возможно, мне нужно первое простое число из списка 1000000 случайных чисел.

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

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

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

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


Примечание:

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


0
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

Самый 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

Это реализация Fisher Yates / Durstenfeld Shuffle , но без фактического создания массива, что снижает сложность пространства или необходимую память, когда размер выбора мал по сравнению с количеством доступных элементов.

Чтобы выбрать 8 чисел из 100, необязательно создавать массив из 100 элементов.

Предполагая, что массив создан,

  • С конца массива (100) получить случайное число ( rnd) от 1 до 100
  • Поменять местами 100 и случайное число rnd
  • Повторите шаг 1 с массивом (99)

Если массив не создан, можно использовать hashMap для запоминания фактических поменяемых местами позиций. Когда второе сгенерированное случайное число равно одному из ранее сгенерированных чисел, карта предоставляет текущее значение в этой позиции, а не фактическое значение.

const getRandom_ = (start, end) => {
  return Math.floor(Math.random() * (end - start + 1)) + start;
};
const getRealValue_ = (map, rnd) => {
  if (map.has(rnd)) {
    return getRealValue_(map, map.get(rnd));
  } else {
    return rnd;
  }
};
const getRandomNumbers = (n, start, end) => {
  const out = new Map();
  while (n--) {
    const rnd = getRandom_(start, end--);
    out.set(getRealValue_(out, rnd), end + 1);
  }
  return [...out.keys()];
};

console.info(getRandomNumbers(8, 1, 100));
console.info(getRandomNumbers(8, 1, Math.pow(10, 12)));
console.info(getRandomNumbers(800000, 1, Math.pow(10, 15)));


0

Вот пример 5 случайных чисел, взятых из диапазона от 0 до 100 (включая 0 и 100) без дублирования.

let finals = [];
const count = 5; // Considering 5 numbers
const max = 100;

for(let i = 0; i < max; i++){
  const rand = Math.round(Math.random() * max);
  !finals.includes(rand) && finals.push(rand)
}

finals = finals.slice(0, count)

-1

Вы также можете сделать это с помощью одного лайнера, например:

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


Для чистоты ничего не назначаю.
— Marcin Król
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.