Genera numeri casuali univoci compresi tra 1 e 100


99

Come posso generare alcuni numeri casuali univoci compresi tra 1 e 100 utilizzando JavaScript?


19
Non proprio un imbroglione dato che si concentra su javascript.
dotty

2
@dotty beh non c'è differenza essenziale tra farlo in Javascript e farlo in qualsiasi altra lingua, ma non voterò per chiudere.
Pointy

1
Non voterò neanche per chiudere. Questo è abbastanza specifico.
Josh Stodola


1
C'è un altro modo più pulito per fare questo stackoverflow.com/questions/51898200/...
Huangism

Risposte:


174

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


15
Il codice effettivo è molto meglio per tali domande rispetto allo pseudocodice;) (cancellata la mia risposta che era pseudocodice ...)
Roman Starkov

3
O può essere raccolto; usa var randomnumber = Math.ceil (Math.random () * 100)
Alsciende

9
-1: questo algoritmo è l'approccio ingenuo; è molto inefficiente.
Frerich Raabe

39
Wow. L'ingenuo sembra un po 'forte. Potrebbe non essere la soluzione migliore, ma è semplice, breve, facile da vedere cosa sta succedendo e funziona entro parametri operativi accettabili per ciò che deve essere realizzato. Passiamo al prossimo compito. La perfezione è fantastica, ma "fatto" è meglio di "perfetto".
adam0101

4
È possibile che la funzione restituisca uno 0 nell'array. Secondo questo collegamento: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… , Math.random () 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.
Qian Chen

43
  1. Popolare un array con i numeri da 1 a 100.
  2. Mescola .
  3. Prendi i primi 8 elementi della matrice risultante.

8
sicuramente è più efficiente modificare il codice per fare solo i primi 8 mescolamenti? (e quindi prendi gli ultimi 8 elementi dell'array semi-mescolato)
secondo

1
Anche io lo faccio sempre così. Quindi, se volessi dieci righe casuali da un file con un mucchio di righe, lo faccio randlines file | head -10.
tchrist

1
Penso che sia la risposta corretta, perché mantiene la distrubtion di probabilità, che la risposta accettata non fa
roberto tomás

2
E se N = 10 ^ 12? Non molto efficiente.
shinzou

2
@shinzou nel mondo reale non ordineresti 10 ^ 12 numeri usando JavaScript. Una domanda faticosa richiede una risposta banale. Non sono qui per risolvere la fame nel mondo. Sono ben attrezzato per farlo, ma non è di questo che si tratta.
ЯegDwight

14

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;

   }
}

CODICE COPIATO DA LINK.

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?


5
Questo approccio è corretto ma non ottimale: potresti smettere di mescolare dopo otto scambi, poiché hai bisogno solo di otto numeri casuali. Il codice precedente scambia l'intero array (in questo scenario, 100 elementi).
Frerich Raabe

Il codice potrebbe essere seriamente migliorato. I valori di ritorno, gli effetti collaterali e l'utilizzo delle funzioni sono tutti molto sfocati. Forse se scrivi una funzione che risponde esattamente al problema originale, usando la tua funzione fisherYates, sarebbe più chiaro.
Alsciende

1
Risposta aggiornata con codice migliorato. Inoltre, @Frerich Raabe: viene menzionato un problema con l'arresto dopo otto scambi.
Pratik Deoghare

Il tuo algoritmo di Fisher-Yates è sbagliato. r dovrebbe dipendere da i. Vedere il mio anwser: stackoverflow.com/questions/2380019/...
Alsciende

Ops scusa il mio orribile errore !! La tua implementazione è fantastica. Mi è piaciuto. +1. Per favore fatemi sapere se c'è qualcos'altro che non va, grazie.
Pratik Deoghare

11

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


Perché è O (n)? Non può eseguire un ciclo per un periodo di tempo arbitrario?
Anthony Wieser

@AnthonyWieser Hai ragione, caso peggiore. Stavo sottintendendo un caso medio poiché Set.add è o (1)
Alister

Penso che questo potrebbe restituire 0 come la mia risposta prima che fosse cambiato per l'usoMath.floor(Math.random()*100) + 1
adam0101

Molto bello da scoprire Setin 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 sortsotto.
Gilad Barner,

10

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


Voto positivo perché non ho mai visto lo snippet di codice di esecuzione prima
surfmuggle

se voglio creare un codice (8 stringhe alfanumeriche casuali) per i coupon, che deve essere univoco, come posso farlo con Chance.js? nota: i coupon verranno effettuati su richiesta, quindi il numero dei codici sarà indefinito
Oscar Yuandinata

@OscarYuandinata è facile, basta farlo 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
Victor Quinn

@VictorQuinn, mi dispiace di non essere stato chiaro. Voglio dire che il codice del coupon sarà una stringa alfanumerica casuale di 8 caratteri, non un array di 8 stringhe alfanumeriche casuali. hahaha ..
Oscar Yuandinata

Oh @OscarYuandinata è molto più facile eh 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"
Victor Quinn

8

Per evitare qualsiasi mescolamento lungo e inaffidabile, farei quanto segue ...

  1. Genera un array che contiene il numero compreso tra 1 e 100, in ordine.
  2. Genera un numero casuale compreso tra 1 e 100
  3. Cerca il numero in questo indice nell'array e memorizza i risultati
  4. Rimuovere l'elemento dall'array, rendendolo più corto
  5. Ripeti dal passaggio 2, ma usa 99 come limite superiore del numero casuale
  6. Ripetere dal passaggio 2, ma utilizzare 98 come limite superiore del numero casuale
  7. Ripetere dal passaggio 2, ma utilizzare 97 come limite superiore del numero casuale
  8. Ripeti dal passaggio 2, ma usa 96 come limite superiore del numero casuale
  9. Ripeti dal passaggio 2, ma utilizza 95 come limite superiore del numero casuale
  10. Ripeti dal passaggio 2, ma utilizza 94 come limite superiore del numero casuale
  11. Ripeti dal passaggio 2, ma usa 93 come limite superiore del numero casuale

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>


Ma poi il tuo ottavo numero è casuale da 1 a 92, non da 1 a 100. Se dovessi scegliere 90 numeri, il tuo ultimo numero verrebbe scelto solo da 1 a 10, no?
adam0101

@ adam0101 No, perché rimuove i numeri man mano che li seleziona. Quindi nel passaggio 5, ci sono solo 99 numeri nella sua matrice. @belugabob Non sei più efficiente di Knuth Shuffle. In effetti, la giunzione è probabilmente più costosa dello shuffle (che è perfettamente affidabile)
Alsciende

@ adam0101: sta rimuovendo l'elemento scelto dall'array (vedere il passaggio 4 sopra), evitando così che qualsiasi elemento venga scelto due volte. Quindi utilizza un limite superiore inferiore per il numero casuale successivo, semplicemente perché l'array è più corto.
Frerich Raabe

@Alsciende, Sì - pensavo che ci sarebbe stato un modo per farlo in modo più efficiente usando uno shuffle, ma non ne ero completamente sicuro. Per evitare di eliminare l'elemento dall'array, copia semplicemente l'ultima voce dall'array (a condizione che non fosse quello che hai scelto) nella posizione da cui hai scelto.
belugabob

1
Il motivo per non decrementare values.length è che non vi è alcuna garanzia che la riduzione della lunghezza di un array non venga eseguita riallocando la memoria. L'uso di maxIndex ha lo stesso effetto, ignorando semplicemente le ultime voci nell'array, poiché diventano irrilevanti.
belugabob

8

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


Questa è la mia risposta preferita tra tutte. Non so perché ha ottenuto solo 6 voti. Elegante e di buona complessità (a patto che sortsia implementato bene, cosa che sono sicuro che sia).
Gilad Barner,

3

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;

3

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.


cosa succederà se voglio che venga incluso 0? la riga min = (min) ? min : 1,restituirà sempre 1. (quindi 0 non sarà mai selezionato)
TBE

Un ottimo punto. :). Grazie, ho apportato la modifica appropriata. Ora tornerà anche se passi con uno 0.
kaizer1v

2

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.


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

più breve di altre risposte che ho visto


1

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

Bella implementazione ricorsiva - Ho pubblicato un'alternativa, nella mia risposta, che non usa la giunzione, poiché ritengo che questo sia un successo di prestazioni evitabile (Non che l'OP abbia avuto problemi con le prestazioni)
belugabob

La soluzione è intelligente, ma io non lo uso nel mio Array # metodo scegliere, perché non voglio questo per avere i suoi elementi mescolate intorno al mio ritorno esso.
Alsciende

Quale array non vuoi che sia mescolato, l'array originale 1-100 oi risultati? Il primo non dovrebbe avere importanza, poiché è un array funzionante, e il secondo, per la natura del codice, uscirà comunque in un ordine casuale. Non sono del tutto sicuro di aver capito le tue ragioni.
belugabob

Quello originale. Ho implementato un metodo generico Array # pick, che trovo utile. Questa funzione non sa se questo è un array funzionante o meno. Per essere generico, non altera questo più del necessario.
Alsciende

Ma lo altera ancora, anche se solo un po ', il che è inevitabile quando si utilizza questa tecnica.
belugabob

1

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

1

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

1

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


0

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;
  }
}

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

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]


0

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.


0

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.


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;
}

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.


0

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

inserisci qui la descrizione dell'immagine

jsFiddle


0

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

0

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 agli Nelementi 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 samplein un modo che non muti l'array di input, ma potresti facilmente sostenere che un'implementazione mutante è favorevole.

Ad esempio, la shufflefunzione 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]

samplenon è 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.

  • "Quanti dovrei provare?" - non devi specificare
  • "Devo prima trovare tutti i numeri primi e poi selezionare un numero primo casuale?" - No.

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, NaNviene 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


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

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


0

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,

  • Dalla fine della matrice (100), ottieni un numero casuale ( rnd) da 1 a 100
  • Scambia 100 e il numero casuale rnd
  • Ripeti il ​​passaggio 1 con array (99)

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


0

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)

-1

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())]


Per la purezza di non assegnare nulla.
Marcin Król
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.