Come posso generare alcuni numeri casuali univoci compresi tra 1 e 100 utilizzando JavaScript?
Come posso generare alcuni numeri casuali univoci compresi tra 1 e 100 utilizzando JavaScript?
Risposte:
Ad esempio: per generare 8 numeri casuali univoci e memorizzarli in un array, puoi semplicemente fare questo:
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)
. Se the Math.random()
restituisce accidentalmente 0, anche il Math.ceil(0)
è 0, sebbene la possibilità sia bassa.
randlines file | head -10
.
Genera permutazione di 100 numeri e poi scegli in serie.
Usa l' algoritmo Knuth Shuffle (noto anche come 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;
}
}
MODIFICA :
Codice migliorato:
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);
}
Potenziale problema:
Supponiamo di avere un array di 100 numeri {eg [1,2,3 ... 100]} e smettiamo di scambiare dopo 8 scambi; quindi la maggior parte delle volte l'array apparirà come {1,2,3,76,5,6,7,8, ... i numeri qui saranno mescolati ... 10}.
Poiché ogni numero verrà scambiato con probabilità 1/100, quindi prob. di scambiare i primi 8 numeri è 8/100 mentre prob. di scambiare altri 92 è 92/100.
Ma se eseguiamo l'algoritmo per l'array completo, siamo sicuri (quasi) che ogni voce viene scambiata.
Altrimenti ci troviamo di fronte a una domanda: quali 8 numeri scegliere?
Soluzione JS moderna che utilizza Set (e caso medio 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
in JS! Tuttavia, questa soluzione non provocherebbe la generazione di numeri non necessaria fino a quando non si soddisfa il requisito di unicità, specialmente nelle ultime iterazioni, se 8 fosse più vicino a 100? Quindi penso di preferire la risposta anche elegante con sort
sotto.
Le tecniche di cui sopra sono buone se vuoi evitare una libreria, ma a seconda che tu stia bene con una libreria, suggerirei di controllare Chance per generare cose casuali in JavaScript.
Nello specifico per risolvere la tua domanda, usare Chance è facile come:
// 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>
Disclaimer, in quanto autore di Chance, sono un po 'di parte;)
var codes = chance.unique(chance.string, 8)
Se hai bisogno dei codici estratti da un particolare pool di caratteri, puoi specificarlo in questo modo: chance.unique(chance.string, 8, {pool: "abcd1234"})
dove abcd1234 può essere qualsiasi carattere tu voglia nel pool. Vedi chancejs.com/#string
chance.string({ length: 8 })
e se vuoi solo che alcuni caratteri appaiano in quella stringa, chance.string({ pool: 'abcd1234', length: 8 })
ciò restituirebbe una stringa casuale di 8 caratteri dai caratteri abcd1234, quindi ad esempio "2c2c44bc" o "331141cc"
Per evitare qualsiasi mescolamento lungo e inaffidabile, farei quanto segue ...
Voilà: nessun numero ripetuto.
Potrei postare del codice effettivo più tardi, se qualcuno è interessato.
Modifica: probabilmente è la mia striscia competitiva ma, dopo aver visto il post di @Alsciende, non ho potuto resistere a pubblicare il codice che avevo promesso.
<!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>
Un altro approccio consiste nel generare un array di 100 elementi con numeri crescenti e ordinarlo in modo casuale. Questo porta effettivamente a uno snippet molto breve e (secondo me) semplice.
const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));
sort
sia implementato bene, cosa che sono sicuro che sia).
Lo farei:
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;
Questa è una funzione molto generica che ho scritto per generare numeri interi univoci / non univoci casuali per un array. Supponiamo che l'ultimo parametro sia vero in questo scenario per questa risposta.
/* 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;
}
Qui il 'tempObj' è un oggetto molto utile poiché ogni numero casuale generato controllerà direttamente in questo tempObj se quella chiave esiste già, in caso contrario, riduciamo la i di uno poiché abbiamo bisogno di 1 esecuzione extra poiché il numero casuale corrente esiste già .
Nel tuo caso, esegui quanto segue
_arrayRandom(8, 1, 100, true);
È tutto.
min = (min) ? min : 1,
restituirà sempre 1. (quindi 0 non sarà mai selezionato)
Mescolare i numeri da 1 a 100 è la giusta strategia di base, ma se hai bisogno di solo 8 numeri mescolati, non c'è bisogno di mescolare tutti i 100 numeri.
Non conosco molto bene Javascript, ma credo che sia facile creare rapidamente un array di 100 null. Quindi, per 8 round, si scambia l'ennesimo elemento dell'array (n che inizia da 0) con un elemento selezionato casualmente da n + 1 a 99. Ovviamente, qualsiasi elemento non ancora popolato significa che l'elemento sarebbe stato davvero l'indice originale più 1, quindi è banale da considerare. Quando hai finito con gli 8 round, i primi 8 elementi della tua matrice avranno i tuoi 8 numeri mescolati.
Stesso algoritmo di permutazione di The Machine Charmer, ma con un'implementazione prototipata. Più adatto a un gran numero di scelte. Utilizza l' assegnazione destrutturante js 1.7 se disponibile.
// 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);
Modifica: un'altra proposta, più adatta a un numero limitato di scelte, basata sulla risposta di belugabob. Per garantire l'unicità, rimuoviamo i numeri selezionati dall'array.
// 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);
per array con buchi come questo [,2,,4,,6,7,,]
perché il mio problema era riempire questi buchi. Quindi l'ho modificato secondo le mie necessità :)
la seguente soluzione modificata ha funzionato per me :)
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
La migliore risposta precedente è la risposta di sje397
. Otterrai il maggior numero possibile di numeri casuali, il più velocemente possibile.
La mia soluzione è molto simile alla sua soluzione. Tuttavia, a volte vuoi i numeri casuali in ordine casuale, ed è per questo che ho deciso di pubblicare una risposta. Inoltre, fornisco una funzione generale.
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));
Ecco la mia versione ES6 che ho messo insieme. Sono sicuro che può essere un po 'più consolidato.
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);
Che ne dici di usare le proprietà degli oggetti come una tabella hash ? In questo modo il tuo scenario migliore è randomizzare solo 8 volte. Sarebbe efficace solo se desideri una piccola parte dell'intervallo di numeri. È anche molto meno dispendioso in termini di memoria rispetto a Fisher-Yates perché non è necessario allocare spazio per un array.
var ht={}, i=rands=8;
while ( i>0 || keys(ht).length<rands) ht[Math.ceil(Math.random()*100)]=i--;
alert(keys(ht));
Ho poi scoperto che Object.keys (obj) è una funzionalità ECMAScript 5, quindi quanto sopra è praticamente inutile su Internet in questo momento. Non temere, perché l'ho reso compatibile con ECMAScript 3 aggiungendo una funzione di tasti come questa.
if (typeof keys == "undefined")
{
var keys = function(obj)
{
props=[];
for (k in ht) if (ht.hasOwnProperty(k)) props.push(k);
return props;
}
}
se hai bisogno di più univoco devi generare un array (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;
}
il codice sopra è più veloce:
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]
Aggiunta di un'altra versione migliore dello stesso codice (risposta accettata) con la funzione indexOf di JavaScript 1.6. Non è necessario eseguire il loop dell'intero array ogni volta che si controlla il duplicato.
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;
}
La versione precedente di Javascript può ancora utilizzare la versione in alto
PS: ho provato a suggerire un aggiornamento al wiki ma è stato rifiutato. Penso ancora che possa essere utile per gli altri.
Questa è la mia soluzione personale:
<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>
Genera in modo casuale 8 valori di array univoci (tra 0 e 7), quindi li visualizza utilizzando una casella di avviso.
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;
}
Penso che questo metodo sia diverso dai metodi forniti nella maggior parte delle risposte, quindi ho pensato di poter aggiungere una risposta qui (anche se la domanda è stata posta 4 anni fa).
Generiamo 100 numeri casuali e etichettiamo ciascuno di essi con numeri da 1 a 100. Quindi ordiniamo questi numeri casuali etichettati e i tag vengono mescolati casualmente. In alternativa, se necessario in questa domanda, si potrebbe fare a meno di trovare i primi 8 numeri casuali contrassegnati. Trovare i primi 8 elementi è più economico che ordinare l'intero array.
Bisogna notare qui che l'algoritmo di ordinamento influenza questo algoritmo. Se l'algoritmo di ordinamento utilizzato è stabile, vi è un leggero pregiudizio a favore di numeri più piccoli. Idealmente, vorremmo che l'algoritmo di ordinamento fosse instabile e nemmeno sbilanciato verso la stabilità (o instabilità) per produrre una risposta con una distribuzione di probabilità perfettamente uniforme.
Questo può gestire la generazione di un numero casuale UNICO fino a 20 cifre
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);
Questa soluzione utilizza l'hash che è molto più performante O (1) rispetto al controllo se risiede nell'array. Ha anche controlli extra sicuri. Spero che sia d'aiuto.
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))
L'implementazione di questo come generatore rende molto piacevole lavorarci. Nota, questa implementazione è diversa da quelle che richiedono prima la mescolanza dell'intero array di input.
Questo
sample
funzione funziona pigramente, dandoti 1 oggetto casuale per iterazione fino agliN
elementi che chiedi. Questo è utile perché se vuoi solo 3 elementi da un elenco di 1000 , non devi prima toccare tutti i 1000 elementi.
// 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]
Ho scelto di implementare sample
in un modo che non muti l'array di input, ma potresti facilmente sostenere che un'implementazione mutante è favorevole.
Ad esempio, la shuffle
funzione potrebbe voler modificare l'array di input originale. Oppure potresti voler campionare dallo stesso input in momenti diversi, aggiornando l'input ogni volta.
// 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
non è più una funzione pura a causa della mutazione dell'input dell'array, ma in determinate circostanze (dimostrate sopra) potrebbe avere più senso.
Un altro motivo per cui ho scelto un generatore invece di una funzione che restituisce solo un array è perché potresti voler continuare il campionamento fino a una condizione specifica.
Forse voglio il primo numero primo da un elenco di 1.000.000 di numeri casuali.
Poiché stiamo lavorando con un generatore, questa operazione è banale
const randomPrimeNumber = listOfNumbers => {
for (let x of sample(Infinity) (listOfNumbers)) {
if (isPrime(x))
return x;
}
return NaN;
}
Questo campionerà continuamente 1 numero casuale alla volta x
, controlla se è primo, quindi ritornax
se lo è. Se l'elenco dei numeri si esaurisce prima che venga trovato un numero primo, NaN
viene restituito.
Nota:
Questa risposta è stata originariamente condivisa su un'altra domanda che è stata chiusa come un duplicato di questa. Poiché è molto diverso dalle altre soluzioni fornite qui, ho deciso di condividerlo anche qui
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
}
Usare a Set
è la tua opzione più veloce. Ecco una funzione generica per ottenere un casuale univoco che utilizza un generatore di callback. Ora è veloce e riutilizzabile .
// 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))
Questa è un'implementazione di Fisher Yates / Durstenfeld Shuffle , ma senza l'effettiva creazione di un array, riducendo così la complessità dello spazio o la memoria necessaria, quando la dimensione del plettro è piccola rispetto al numero di elementi disponibili.
Per scegliere 8 numeri da 100, non è necessario creare un array di 100 elementi.
Supponendo che venga creato un array,
rnd
) da 1 a 100 rnd
Se non viene creato un array, è possibile utilizzare A hashMap per ricordare le posizioni scambiate effettive. Quando il secondo numero casuale generato è uguale a quello dei numeri generati in precedenza, la mappa fornisce il valore corrente in quella posizione piuttosto che il valore effettivo.
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)));
Ecco un esempio di 5 numeri casuali presi da un intervallo da 0 a 100 (sia 0 che 100 inclusi) senza duplicazione.
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)
Puoi anche farlo con un rivestimento come questo:
[...((add, set) => add(set, add))((set, add) => set.size < 8 ? add(set.add(Math.floor(Math.random()*100) + 1), add) : set, new Set())]