Генерация уникальных случайных чисел от 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.