Generazione di numeri casuali univoci in Java


88

Sto cercando di ottenere numeri casuali compresi tra 0 e 100. Ma voglio che siano univoci, non ripetuti in una sequenza. Ad esempio, se ho 5 numeri, dovrebbero essere 82,12,53,64,32 e non 82,12,53,12,32. Ho usato questo, ma genera gli stessi numeri in sequenza.

Random rand = new Random();
selected = rand.nextInt(100);

5
Potresti creare una permutazione casuale dell'intervallo 1..100(ci sono algoritmi famosi per questo), ma fermati dopo aver determinato i primi nelementi.
Kerrek SB



Questo potrebbe essere utile Generatore di ID casuali univoci
Erfan Ahmed

Risposte:


145
  • Aggiungi ogni numero nell'intervallo in sequenza in una struttura a elenco .
  • Mescola .
  • Prendi la prima "n".

Ecco una semplice implementazione. Questo stamperà 3 numeri casuali univoci nell'intervallo 1-10.

import java.util.ArrayList;
import java.util.Collections;

public class UniqueRandomNumbers {

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i=1; i<11; i++) {
            list.add(new Integer(i));
        }
        Collections.shuffle(list);
        for (int i=0; i<3; i++) {
            System.out.println(list.get(i));
        }
    }
}

La prima parte della correzione con l'approccio originale, come ha sottolineato Mark Byers in una risposta ora eliminata, consiste nell'utilizzare solo una singola Randomistanza.

Questo è ciò che fa sì che i numeri siano identici. Viene Randomeseguito il seeding di un'istanza in base all'ora corrente in millisecondi. Per un particolare valore seme, l'istanza "casuale" restituirà la stessa identica sequenza di numeri pseudo casuali .

NOTA che il public Integer​(int value)costruttore è deprecatedda Java 9.

Il primo ciclo for può essere semplicemente modificato in:

for (int i = 1; i < 11; i++) {
  list.add(i);
}

3
+1 per aver indicato una singola istanza casuale e aver risposto alla domanda. :)
Mark Byers

Non è necessario mescolare l'intero intervallo. Se vuoi n numeri univoci, devi solo mescolare la prima n posizione usando un mescolamento Fisher-Yates. Questo può aiutare con un lungo elenco e un piccolo n.
rossum

59

Con Java 8+ è possibile utilizzare il intsmetodo Randomper ottenere quindi un numero IntStreamdi valori casuali distincte limitridurre il flusso a un numero di valori casuali univoci.

ThreadLocalRandom.current().ints(0, 100).distinct().limit(5).forEach(System.out::println);

Randomha anche metodi che creano LongStreams e DoubleStreams se invece ne hai bisogno.

Se vuoi tutti (o una grande quantità) dei numeri in un intervallo in un ordine casuale, potrebbe essere più efficiente aggiungere tutti i numeri a un elenco, mescolarlo e prendere la prima n perché l'esempio sopra è attualmente implementato generando numeri casuali nell'intervallo richiesto e passandoli attraverso un set (in modo simile alla risposta di Rob Kielty ), che potrebbe richiedere la generazione di molti di più rispetto all'importo passato al limite perché la probabilità di generare un nuovo numero univoco diminuisce con ogni trovata. Ecco un esempio dell'altro modo:

List<Integer> range = IntStream.range(0, 100).boxed()
        .collect(Collectors.toCollection(ArrayList::new));
Collections.shuffle(range);
range.subList(0, 99).forEach(System.out::println);

Ne avevo bisogno per un po 'di codice che sto valutando ed Arrays#setAll()è un po' più veloce di un flusso. Quindi: `Integer [] indices = new Integer [n]; Arrays.setAll (indici, i -> i); Collections.shuffle (Arrays.asList (indices)); return Arrays.stream (indices) .mapToInt (Integer :: intValue) .toArray (); `
AbuNassar,

18
  1. Crea un array di 100 numeri, quindi randomizza il loro ordine.
  2. Crea un generatore di numeri pseudo-casuali che abbia un intervallo di 100.
  3. Crea un array booleano di 100 elementi, quindi imposta un elemento vero quando scegli quel numero. Quando scegli il numero successivo, controlla l'array e riprova se l'elemento dell'array è impostato. (È possibile creare un array booleano facile da cancellare con un array di longdove si sposta e maschera per accedere ai singoli bit.)

2
+1 per l'approccio alternativo; pick()è un esempio.
trashgod

1
Invece di usare un array booleano, puoi usare a HashSet, dove memorizzi i numeri che hai già generato e usalo containsper verificare se hai già generato quel numero. La HashSetprobabilmente sarà leggermente più lento rispetto a un array booleano, ma occupano meno memoria.
Rory O'Kane

1
@ RoryO'Kane - Sono abbastanza sicuro che l'array booleano occuperebbe meno spazio, se implementato come array di long [2]. In nessun modo potresti creare un HashSet così piccolo.
Hot Licks

L'ultimo approccio è un po 'brutto in quanto non avrebbe un numero ben definito di passaggi per generare l'intera sequenza. Inoltre non è necessario reinventare la ruota: BitSet .
Pavel Horal

16

Utilizzare Collections.shuffle()su tutti i 100 numeri e selezionare i primi cinque, come mostrato qui .


13

Penso che questo metodo valga la pena menzionare.

   private static final Random RANDOM = new Random();    
   /**
     * Pick n numbers between 0 (inclusive) and k (inclusive)
     * While there are very deterministic ways to do this,
     * for large k and small n, this could be easier than creating
     * an large array and sorting, i.e. k = 10,000
     */
    public Set<Integer> pickRandom(int n, int k) {
        final Set<Integer> picked = new HashSet<>();
        while (picked.size() < n) {
            picked.add(RANDOM.nextInt(k + 1));
        }
        return picked;
    }

9

Ho ri-fattorizzato la risposta di Anand per utilizzare non solo le proprietà uniche di un set, ma anche il valore booleano false restituito da set.add()quando un'aggiunta al set fallisce.

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class randomUniqueNumberGenerator {

    public static final int SET_SIZE_REQUIRED = 10;
    public static final int NUMBER_RANGE = 100;

    public static void main(String[] args) {
        Random random = new Random();

        Set set = new HashSet<Integer>(SET_SIZE_REQUIRED);

        while(set.size()< SET_SIZE_REQUIRED) {
            while (set.add(random.nextInt(NUMBER_RANGE)) != true)
                ;
        }
        assert set.size() == SET_SIZE_REQUIRED;
        System.out.println(set);
    }
}

1
Bella idea. Un segno importante però - se SET_SIZE_REQUIREDè abbastanza grande (diciamo, più di NUMBER_RANGE / 2allora hai un tempo di esecuzione previsto molto maggiore.
noamgot

5

L'ho fatto così.

    Random random = new Random();
    ArrayList<Integer> arrayList = new ArrayList<Integer>();

    while (arrayList.size() < 6) { // how many numbers u need - it will 6
        int a = random.nextInt(49)+1; // this will give numbers between 1 and 50.

        if (!arrayList.contains(a)) {
            arrayList.add(a);
        }
    }

4

Questo funzionerà per generare numeri casuali univoci ................

import java.util.HashSet;
import java.util.Random;

public class RandomExample {

    public static void main(String[] args) {
        Random rand = new Random();
        int e;
        int i;
        int g = 10;
        HashSet<Integer> randomNumbers = new HashSet<Integer>();

        for (i = 0; i < g; i++) {
            e = rand.nextInt(20);
            randomNumbers.add(e);
            if (randomNumbers.size() <= 10) {
                if (randomNumbers.size() == 10) {
                    g = 10;
                }
                g++;
                randomNumbers.add(e);
            }
        }
        System.out.println("Ten Unique random numbers from 1 to 20 are  : " + randomNumbers);
    }
}

3

Un modo intelligente per farlo è usare gli esponenti di un elemento primitivo nel modulo.

Ad esempio, 2 è una radice primitiva mod 101, il che significa che i poteri di 2 mod 101 ti danno una sequenza non ripetitiva che vede ogni numero da 1 a 100 inclusi:

2^0 mod 101 = 1
2^1 mod 101 = 2
2^2 mod 101 = 4
...
2^50 mod 101 = 100
2^51 mod 101 = 99
2^52 mod 101 = 97
...
2^100 mod 101 = 1

Nel codice Java, dovresti scrivere:

void randInts() {
int num=1;
for (int ii=0; ii<101; ii++) {
    System.out.println(num);
    num= (num*2) % 101;
    }
}

Trovare una radice primitiva per un modulo specifico può essere complicato, ma la funzione "primroot" di Maple lo farà per te.


È interessante, ma come possiamo assicurarci che la sequenza generata sia casuale? Non sembra esserlo. Sembra molto deterministico avere 1,2,4,8,16, ... all'inizio di una sequenza.
h4nek

Non è casuale ... è pseudo-casuale. Nessuno sa come generare numeri veramente casuali. Se non ti piace il pattern iniziale, puoi usare una base più grande come radice primitiva.
AT - studente

Pseudo-casuale andrebbe bene. Ma qui, per un dato "intervallo", la quantità di radici primitive e quindi di sequenze uniche è limitata, specialmente per intervalli più piccoli. Quindi sembra esserci un problema con il modello, ad esempio che ha sempre una sottosequenza di poteri della radice. E non ottenendo una sequenza (probabilmente) molto diversa su più esecuzioni, a meno che non applichiamo altri imbrogli. Immagino che dipenda dal caso d'uso. Cambiare la base è comunque un bel aggiornamento, anche se "sposta" solo lo schema.
h4nek

2

Sono venuto qui da un'altra domanda, che è stata duplicata di questa domanda ( Generazione di un numero casuale univoco in java )

  1. Memorizza da 1 a 100 numeri in un array.

  2. Genera un numero casuale compreso tra 1 e 100 come posizione e restituisci la matrice [posizione-1] per ottenere il valore

  3. Dopo aver utilizzato un numero nell'array, contrassegnare il valore come -1 (non è necessario mantenere un altro array per verificare se questo numero è già utilizzato)

  4. Se il valore nell'array è -1, ottieni di nuovo il numero casuale per recuperare la nuova posizione nell'array.


2

Ho una soluzione semplice per questo problema, con questo possiamo facilmente generare n numero di numeri casuali univoci, è solo logica che chiunque può usarlo in qualsiasi lingua.

for(int i=0;i<4;i++)
        {
            rn[i]= GenerateRandomNumber();
            for (int j=0;j<i;j++)
            {
                if (rn[i] == rn[j])
                {
                    i--;
                }
            }
        }

puoi ottimizzare facendo un break;dopoi—;
Jan

0

prova questo

public class RandomValueGenerator {
    /**
     * 
     */
    private volatile List<Double> previousGenValues = new ArrayList<Double>();

    public void init() {
        previousGenValues.add(Double.valueOf(0));
    }

    public String getNextValue() {
        Random random = new Random();
        double nextValue=0;
        while(previousGenValues.contains(Double.valueOf(nextValue))) {
            nextValue = random.nextDouble();
        }
        previousGenValues.add(Double.valueOf(nextValue));
        return String.valueOf(nextValue);
    }
}

0

Questo non è significativamente diverso dalle altre risposte, ma alla fine volevo l'array di numeri interi:

    Integer[] indices = new Integer[n];
    Arrays.setAll(indices, i -> i);
    Collections.shuffle(Arrays.asList(indices));
    return Arrays.stream(indices).mapToInt(Integer::intValue).toArray();

0

puoi usare un array booleano per riempire il vero se il valore è stato preso altrimenti impostare navigare attraverso l'array booleano per ottenere il valore come indicato di seguito

package study;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
Created By Sachin  Rane on Jul 18, 2018
*/
public class UniqueRandomNumber {
    static Boolean[] boolArray;
    public static void main(String s[]){
        List<Integer> integers = new ArrayList<>();


        for (int i = 0; i < 10; i++) {
            integers.add(i);
        }


        //get unique random numbers
        boolArray = new Boolean[integers.size()+1];
        Arrays.fill(boolArray, false);
        for (int i = 0; i < 10; i++) {
            System.out.print(getUniqueRandomNumber(integers) + " ");

        }

    }

    private static int  getUniqueRandomNumber(List<Integer> integers) {
        int randNum =(int) (Math.random()*integers.size());
        if(boolArray[randNum]){
            while(boolArray[randNum]){
                randNum++;
                if(randNum>boolArray.length){
                    randNum=0;
                }
            }
            boolArray[randNum]=true;
            return randNum;
        }else {
            boolArray[randNum]=true;
            return randNum;
        }

    }

}

0

Scegli n numeri casuali univoci da 0 a m-1.

int[] uniqueRand(int n, int m){
    Random rand = new Random();
    int[] r = new int[n];
    int[] result = new int[n];
    for(int i = 0; i < n; i++){
        r[i] = rand.nextInt(m-i);
        result[i] = r[i];
        for(int j = i-1; j >= 0; j--){
            if(result[i] >= r[j])
                result[i]++;
        }
    }
    return result;
}

Immagina un elenco contenente numeri da 0 a m-1. Per scegliere il primo numero, usiamo semplicemente rand.nextInt(m). Quindi rimuovere il numero dall'elenco. Ora rimangono numeri m-1, quindi chiamiamorand.nextInt(m-1) . Il numero che otteniamo rappresenta la posizione nell'elenco. Se è inferiore al primo numero, allora è il secondo numero, poiché la parte dell'elenco precedente al primo numero non è stata modificata dalla rimozione del primo numero. Se la posizione è maggiore o uguale al primo numero, il secondo numero è la posizione + 1. Fai qualche ulteriore derivazione, puoi ottenere questo algoritmo.

Spiegazione

Questo algoritmo ha complessità O (n ^ 2). Quindi è utile per generare piccole quantità di numeri univoci da un insieme di grandi dimensioni. Mentre l'algoritmo basato su shuffle richiede almeno O (m) per eseguire lo shuffle.

Anche l'algoritmo basato su shuffle ha bisogno di memoria per memorizzare ogni possibile risultato per eseguire lo shuffle, questo algoritmo non ha bisogno.


0

Puoi usare la classe Collections.

Una classe di utilità chiamata Collections offre diverse azioni che possono essere eseguite su una raccolta come un ArrayList (ad esempio, cercare gli elementi, trovare l'elemento massimo o minimo, invertire l'ordine degli elementi e così via). Una delle azioni che può eseguire è mescolare gli elementi. Lo shuffle sposterà casualmente ogni elemento in una posizione diversa nell'elenco. Lo fa utilizzando un oggetto Random. Ciò significa che è casualità deterministica, ma lo farà nella maggior parte delle situazioni.

Per mescolare ArrayList, aggiungi l'importazione delle raccolte all'inizio del programma e quindi utilizza il metodo statico Shuffle. Richiede ArrayList da mescolare come parametro:

import java.util.Collections;
import java.util.ArrayList;
public class Lottery {
public static void main(String[] args) {
//define ArrayList to hold Integer objects
ArrayList numbers = new ArrayList();
for(int i = 0; i < 100; i++)
{
numbers.add(i+1);
}
Collections.shuffle(numbers);
System.out.println(numbers);
}
}

0

Sebbene sia un vecchio thread, l'aggiunta di un'altra opzione potrebbe non essere dannosa. (Le funzioni lambda JDK 1.8 sembrano renderlo facile);

Il problema potrebbe essere suddiviso nei seguenti passaggi;

  • Ottieni un valore minimo per l'elenco fornito di numeri interi (per i quali generare numeri casuali univoci)
  • Ottieni un valore massimo per l'elenco di numeri interi fornito
  • Utilizzare la classe ThreadLocalRandom (da JDK 1.8) per generare valori interi casuali rispetto ai valori interi minimo e massimo trovati in precedenza e quindi filtrare per assicurarsi che i valori siano effettivamente contenuti nell'elenco fornito originariamente. Infine applica distinto all'intstream per garantire che i numeri generati siano univoci.

Ecco la funzione con qualche descrizione:

/**
 * Provided an unsequenced / sequenced list of integers, the function returns unique random IDs as defined by the parameter
 * @param numberToGenerate
 * @param idList
 * @return List of unique random integer values from the provided list
 */
private List<Integer> getUniqueRandomInts(List<Integer> idList, Integer numberToGenerate) {

    List<Integer> generatedUniqueIds = new ArrayList<>();

    Integer minId = idList.stream().mapToInt (v->v).min().orElseThrow(NoSuchElementException::new);
    Integer maxId = idList.stream().mapToInt (v->v).max().orElseThrow(NoSuchElementException::new);

            ThreadLocalRandom.current().ints(minId,maxId)
            .filter(e->idList.contains(e))
            .distinct()
            .limit(numberToGenerate)
            .forEach(generatedUniqueIds:: add);

    return generatedUniqueIds;

}

Quindi, per ottenere 11 numeri casuali univoci per l'oggetto elenco 'allIntegers', chiameremo la funzione come;

    List<Integer> ids = getUniqueRandomInts(allIntegers,11);

La funzione dichiara new arrayList 'generatedUniqueIds' e viene popolata con ogni numero intero casuale univoco fino al numero richiesto prima di restituire.

La classe PS ThreadLocalRandom evita il valore di inizializzazione comune in caso di thread simultanei.


0

Questo è il metodo più semplice per generare valori casuali univoci in un intervallo o da un array .

In questo esempio, userò un array predefinito ma puoi adattare questo metodo anche per generare numeri casuali. Innanzitutto, creeremo un array di esempio da cui recuperare i nostri dati.

  1. Genera un numero casuale e aggiungilo al nuovo array.
  2. Genera un altro numero casuale e controlla se è già memorizzato nel nuovo array.
  3. In caso contrario, aggiungilo e continua
  4. altrimenti ribadisco il passaggio.
ArrayList<Integer> sampleList = new ArrayList<>();
sampleList.add(1);
sampleList.add(2);
sampleList.add(3);
sampleList.add(4);
sampleList.add(5);
sampleList.add(6);
sampleList.add(7);
sampleList.add(8);

Ora da sampleListnoi produrremo cinque numeri casuali che sono unici.

int n;
randomList = new ArrayList<>();
for(int  i=0;i<5;i++){
    Random random = new Random();
    n=random.nextInt(8);     //Generate a random index between 0-7

    if(!randomList.contains(sampleList.get(n)))
    randomList.add(sampleList.get(n));
    else
        i--;    //reiterating the step
}
        

Questo è concettualmente molto semplice. Se il valore casuale generato esiste già, ripeteremo il passaggio. Ciò continuerà fino a quando tutti i valori generati saranno univoci.

Se hai trovato utile questa risposta, puoi votarla perché è molto semplice nel concetto rispetto alle altre risposte .


-1

È possibile generare n numero casuale univoco compreso tra 0 e n-1 in java

public static void RandomGenerate(int n)
{
     Set<Integer> st=new HashSet<Integer>();
     Random r=new Random();
     while(st.size()<n)
     {
        st.add(r.nextInt(n));
     }

}


-2

Verificare questo

public class RandomNumbers {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int n = 5;
        int A[] = uniqueRandomArray(n);
        for(int i = 0; i<n; i++){
            System.out.println(A[i]);
        }
    }
    public static int[] uniqueRandomArray(int n){
        int [] A = new int[n];
        for(int i = 0; i< A.length; ){
            if(i == A.length){
                break;
            }
            int b = (int)(Math.random() *n) + 1;
            if(f(A,b) == false){
                A[i++] = b;
            } 
        }
        return A;
    }
    public static boolean f(int[] A, int n){
        for(int i=0; i<A.length; i++){
            if(A[i] == n){
                return true;
            }
        }
        return false;
    }
}

2
Gettare gli standard java, la leggibilità e l'usabilità fuori dalla finestra eh?
Austin Wernli

Il codice non è una risposta .. Scrivi una risposta e poi aggiungi il codice per spiegare cosa volevi.
Aditya

-2

Di seguito è riportato un modo che ho utilizzato per generare sempre un numero univoco. La funzione casuale genera un numero e lo memorizza nel file di testo, quindi la prossima volta che lo controlla in file lo confronta e genera un nuovo numero univoco, quindi in questo modo c'è sempre un nuovo numero univoco.

public int GenerateRandomNo()
{
    int _min = 0000;
    int _max = 9999;
    Random _rdm = new Random();
    return _rdm.Next(_min, _max);
}
public int rand_num()
{
    randnum = GenerateRandomNo();
    string createText = randnum.ToString() + Environment.NewLine;
    string file_path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + @"\Invoices\numbers.txt";
    File.AppendAllText(file_path, createText);
    int number = File.ReadLines(file_path).Count(); //count number of lines in file
    System.IO.StreamReader file = new System.IO.StreamReader(file_path);
    do
    {
        randnum = GenerateRandomNo();
    }
    while ((file.ReadLine()) == randnum.ToString());
    file.Close();
    return randnum;

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