Створюйте унікальні випадкові числа від 1 до 100


99

Як я можу створити кілька унікальних випадкових чисел від 1 до 100 за допомогою JavaScript?


19
Насправді не дурень, оскільки це зосереджується на javascript.
dotty

2
@dotty ну немає суттєвої різниці між тим, як робити це в Javascript, і робити це будь-якою іншою мовою, але я не буду голосувати за закриття.
Пойнті

1
Я також не буду голосувати за закриття. Це досить конкретно.
Джош Стодола


1
Існує ще один прибиральник спосіб зробити це stackoverflow.com/questions/51898200 / ...
Huangism

Відповіді:


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
О можна вибрати; використовувати var randomnumber = Math.ceil (Math.random () * 100)
Alsciende

9
-1: цей алгоритм є наївним підходом; це дуже неефективно.
Фріріх Раабе

39
Ого. Наївна здається трохи сильною. Це може бути не найкращим рішенням, але це просто, коротко, легко зрозуміти, що відбувається, і працює в межах прийнятних робочих параметрів для того, що потрібно досягти. Перехід до наступного завдання. Досконалість - це чудово, але «зроблено» краще, ніж «досконало».
adam0101

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.
christ

1
Я думаю, що це правильна відповідь, тому що вона підтримує ймовірність спотворення, а прийнята відповідь - ні,
Роберто Томас

2
Що робити, якщо N = 10 ^ 12? Не дуже ефективно.
shinzou

2
@shinzou в реальному світі ви б не сортували 10 ^ 12 цифр за допомогою JavaScript. Питання тирила вимагає тривіальної відповіді. Я не тут, щоб вирішити світовий голод. Я добре підготовлений для цього, але питання не в цьому.
ЯegDwight

14

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

Використовуйте алгоритм Knuth Shuffle (він же перетасовка Fisher-Yates) .

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 елементів).
Фріріх Раабе,

Код можна було б серйозно вдосконалити. Повернені значення, побічні ефекти та використання функцій - це дійсно розмито IMO. Можливо, якщо ви напишете функцію, яка точно відповідає вихідній проблемі, використовуючи вашу функцію fisherYates, це було б зрозуміліше.
Alsciende

1
Відповідь оновлено покращеним кодом. Також @Frerich Raabe: згадується проблема із зупинкою після восьми обмінів.
Pratik Deoghare

Ваш алгоритм Фішер-Йейтса помилковий. r має залежати від i. Дивіться мій anwser: stackoverflow.com/questions/2380019 / ...
Alsciende

На жаль, вибачте за мою жахливу помилку !! Ваша реалізація класна. Сподобалось. +1. Будь ласка, дайте мені знати, якщо з цим ще щось не так. Дякую.
Pratik Deoghare

11

Сучасне рішення JS з використанням набору (та середнього випадку 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 is o (1)
Алістер,

Я думаю, що це може повернути 0, як моя відповідь раніше, ніж її змінили на використанняMath.floor(Math.random()*100) + 1
adam0101

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

10

Вищевказані методи хороші, якщо ви хочете уникати бібліотеки, але залежно від того, чи добре ви з бібліотекою, я пропоную перевірити шанс для створення випадкових матеріалів у 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

@ adam0101 Ні, бо він видаляє номери, коли їх набирає. Отже, на кроці 5 у його масиві лише 99 чисел. @belugabob Ви не ефективніші за Knuth Shuffle. Насправді сплайс, мабуть, дорожчий за перетасовку (що є цілком надійним)
Alsciende,

@ adam0101: Він видаляє вибраний елемент із масиву (див. крок 4 вище), тим самим уникаючи того, що будь-які елементи обираються двічі. Потім він використовує нижню верхню межу для наступного випадкового числа, просто тому, що масив коротший.
Фріріх Раабе

@Alsciende, так - думав, що буде спосіб зробити це ефективніше за допомогою перетасовки, але не був повністю впевнений. Щоб уникнути видалення елемента з масиву, просто скопіюйте останній запис із масиву (за умови, що це не той, який ви вибрали) у позицію, з якої ви вибрали.
belugabob

1
Причина не зменшення values.length полягає в тому, що немає гарантії, що зменшення довжини масиву не відбувається за рахунок перерозподілу пам'яті. Використання maxIndex має той самий ефект, просто ігноруючи останні записи в масиві, оскільки вони стають неактуальними.
belugabob

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' є дуже корисним obj, оскільки кожне згенероване випадкове число буде безпосередньо перевіряти цей 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);

Редагувати: Інша пропозиція, яка більше підходить для невеликої кількості виборів, заснована на відповіді belugabob. Щоб гарантувати унікальність, ми видаляємо вибрані числа з масиву.

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

Приємна рекурсивна реалізація - у своїй відповіді я опублікував альтернативу, яка не використовує сплайсинг, оскільки я вважаю, що це можливий результат, якого можна уникнути (Не те, щоб у ОП були проблеми з продуктивністю)
belugabob

Ваше рішення розумно, але я не буду використовувати його в моєму масиві # вибрати метод , тому що я не хочу цього , щоб його елементи перемішуються навколо , коли я повернути його.
Alsciende

Який масив ви не хочете перетасовувати, оригінальний масив 1-100 чи результати? Перший не повинен мати значення, оскільки це робочий масив, а другий за природою коду все одно вийде у випадковому порядку. Не зовсім впевнений, що я розумію ваші причини.
belugabob

Оригінальний. Я застосував загальний метод вибору Array #, який я вважаю корисним. Ця функція не знає, це робочий масив чи ні. Щоб бути загальним, це не змінює цього більше, ніж потрібно.
Alsciende

Але це все одно змінює його, навіть хоча б трохи, що є неможливим при використанні цієї техніки.
belugabob

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

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

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


Ще однією причиною, що я вибрав генератор замість функції, яка просто повертає масив, є те, що ви можете продовжити вибірку до певної умови.

Можливо, я хочу перше просте число зі списку 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
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

Використання a 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.