Як я можу створити кілька унікальних випадкових чисел від 1 до 100 за допомогою JavaScript?
Як я можу створити кілька унікальних випадкових чисел від 1 до 100 за допомогою JavaScript?
Відповіді:
Наприклад: Щоб сформувати 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);
Returns a random number between 0 (inclusive) and 1 (exclusive)
. Якщо the Math.random()
випадково поверне 0, значення Math.ceil(0)
також дорівнює 0, хоча шанс низький.
randlines file | head -10
.
Створіть перестановку з 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 чисел вибрати?
Сучасне рішення JS з використанням набору (та середнього випадку O (n))
const nums = new Set();
while(nums.size !== 8) {
nums.add(Math.floor(Math.random() * 100) + 1);
}
console.log([...nums]);
Math.floor(Math.random()*100) + 1
Set
в JS! Однак чи не спричинить це рішення непотрібне генерування чисел, доки не буде відповідати вимогам унікальності, особливо в останніх ітераціях, якби 8 було ближче до 100? Тому я вважаю, що я віддаю перевагу також елегантній відповіді sort
нижче.
Вищевказані методи хороші, якщо ви хочете уникати бібліотеки, але залежно від того, чи добре ви з бібліотекою, я пропоную перевірити шанс для створення випадкових матеріалів у 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, я трохи упереджений;)
var codes = chance.unique(chance.string, 8)
Якщо вам потрібні коди, витягнуті з певного пулу символів, ви можете вказати це так: chance.unique(chance.string, 8, {pool: "abcd1234"})
де abcd1234 можуть бути будь-які символи, які ви хочете в пулі. Див. Chancejs.com/#string
chance.string({ length: 8 })
і якщо ви хочете, щоб у цьому рядку з'явилися лише певні символи, chance.string({ pool: 'abcd1234', length: 8 })
які повернули б випадковий рядок із 8 символів із символів abcd1234, наприклад, "2c2c44bc" або "331141cc"
Щоб уникнути тривалих і ненадійних перетасовк, я б зробив наступне ...
Вуаля - немає повторюваних цифр.
Я можу опублікувати фактичний код пізніше, якщо хтось зацікавиться.
Редагувати: Це, мабуть, конкурентна смуга в мені, але, побачивши допис @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>
Інший підхід полягає у створенні масиву із 100 елементів із зростаючими числами та довільному сортуванні. Це фактично призводить до дійсно короткого та (на мій погляд) простого фрагмента.
const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));
sort
він реалізований добре, що я впевнений, що це так).
Я б зробив це:
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;
Це дуже загальна функція, яку я написав для генерації випадкових унікальних / неунікальних цілих чисел для масиву. Припустимо, що останній параметр відповідає дійсності в цьому випадку для цієї відповіді.
/* 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);
Це все.
min = (min) ? min : 1,
завжди повертає 1. (тому 0 ніколи не буде обрано)
Перестановка чисел з 1 на 100 - правильна основна стратегія, але якщо вам потрібно лише 8 перетасованих чисел, немає необхідності перетасовувати всі 100 чисел.
Я не дуже добре знаю Javascript, але я вважаю, що легко швидко створити масив із 100 нулів. Потім протягом 8 раундів ви поміняєте місцями n-й елемент масиву (n, починаючи з 0) випадковим чином вибраним елементом від n + 1 до 99. Звичайно, будь-які елементи, які ще не заповнені, означають, що елемент справді був би оригінальний індекс плюс 1, тому це тривіально врахувати. Коли ви закінчите з 8 раундами, перші 8 елементів вашого масиву матимуть ваші 8 перетасованих чисел.
Той самий алгоритм перестановки, що і 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);
для масивів з такими отворами, [,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
Найкраща попередня відповідь - це відповідь від 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));
Ось моя версія 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);
Як щодо використання властивостей об’єкта як хеш-таблиці ? Таким чином, ваш найкращий сценарій - рандомизувати лише 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;
}
}
якщо вам потрібен більш унікальний, ви повинні створити масив (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]
Додавання ще однієї кращої версії того самого коду (прийнята відповідь) з функцією 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: Спробував запропонувати оновлення вікі, але його відхилили. Я все ще думаю, що це може бути корисно для інших.
Це моє особисте рішення:
<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), а потім відображає їх за допомогою вікна попередження.
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 найменувань дешевший, ніж сортування цілого масиву.
Тут слід зазначити, що алгоритм сортування впливає на цей алгоритм. Якщо застосовуваний алгоритм сортування стабільний, існує невеликий ухил на користь менших чисел. В ідеалі ми хотіли б, щоб алгоритм сортування був нестабільним і навіть не упередженим до стабільності (або нестабільності), щоб отримати відповідь із абсолютно рівномірним розподілом ймовірностей.
Це може обробляти до 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);
У цьому рішенні використовується хеш, який є набагато ефективнішим 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))
Здійснюючи це як генератор, дуже приємно працювати. Зверніть увагу, що ця реалізація відрізняється від тих, які вимагають спочатку перемішування всього масиву вводу.
Ця
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
повертається.
Примітка:
Ця відповідь спочатку була поширена на інше питання, яке було закрито як дублікат цього. Оскільки воно сильно відрізняється від інших рішень, представлених тут, я вирішив поділитися ним і тут
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
}
Використання 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))
Це реалізація Fisher Yates / Durstenfeld Shuffle , але без фактичного створення масиву, тим самим зменшуючи складність простору або необхідну пам’ять, коли розмір вибору невеликий порівняно з кількістю доступних елементів.
Щоб вибрати 8 чисел із 100, необов’язково створювати масив із 100 елементів.
Припускаючи, що створений масив,
rnd
) від 1 до 100 rnd
Якщо масив не створений, може використовуватися 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)));
Ось приклад випадкових 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)
Ви також можете зробити це за допомогою одного вкладиша, подібного до цього:
[...((add, set) => add(set, add))((set, add) => set.size < 8 ? add(set.add(Math.floor(Math.random()*100) + 1), add) : set, new Set())]