Creazione di numeri casuali senza duplicati


88

In questo caso il MAX è solo 5, quindi potrei controllare i duplicati uno per uno, ma come posso farlo in modo più semplice? Ad esempio, cosa succede se MAX ha un valore di 20? Grazie.

int MAX = 5;

for (i = 1 , i <= MAX; i++)
{
        drawNum[1] = (int)(Math.random()*MAX)+1;

        while (drawNum[2] == drawNum[1])
        {
             drawNum[2] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[3] == drawNum[1]) || (drawNum[3] == drawNum[2]) )
        {
             drawNum[3] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[4] == drawNum[1]) || (drawNum[4] == drawNum[2]) || (drawNum[4] == drawNum[3]) )
        {
             drawNum[4] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[5] == drawNum[1]) ||
               (drawNum[5] == drawNum[2]) ||
               (drawNum[5] == drawNum[3]) ||
               (drawNum[5] == drawNum[4]) )
        {
             drawNum[5] = (int)(Math.random()*MAX)+1;
        }

}

3
Molti (pseudo) generatori di numeri casuali non si ripetono per il loro "ciclo" completo. Il problema è, ovviamente, che il loro "ciclo" completo è di miliardi o trilioni di valori, e i valori che producono possono essere uno qualsiasi di quei miliardi o trilioni di valori. In teoria potresti produrre un generatore di numeri casuali con un "ciclo" di 5 o 10 o altro, ma probabilmente è più un problema di quanto valga la pena.
Hot Licks

1
Anche un generatore di numeri casuali che non si ripete è anche "meno" casuale: se MAX = 5 e leggi 3 numeri, puoi indovinare il successivo con il 50% di probabilità, se leggi 4 numeri, conosci il successivo per il 100% sicuro!
icza

Ha risposto a una domanda duplicata qui
Alex - GlassEditor.com


Risposte:


149

Il modo più semplice sarebbe creare un elenco dei possibili numeri (1..20 o altro) e poi mescolarli con Collections.shuffle. Quindi prendi tutti gli elementi che desideri. Questo è ottimo se il tuo range è uguale al numero di elementi che ti servono alla fine (ad esempio per mescolare un mazzo di carte).

Non funziona così bene se vuoi (diciamo) 10 elementi casuali nell'intervallo 1..10.000 - finiresti per fare un sacco di lavoro inutilmente. A quel punto, probabilmente è meglio mantenere un insieme di valori che hai generato finora e continuare a generare numeri in un ciclo fino a quando il prossimo non è già presente:

if (max < numbersNeeded)
{
    throw new IllegalArgumentException("Can't ask for more numbers than are available");
}
Random rng = new Random(); // Ideally just create one instance globally
// Note: use LinkedHashSet to maintain insertion order
Set<Integer> generated = new LinkedHashSet<Integer>();
while (generated.size() < numbersNeeded)
{
    Integer next = rng.nextInt(max) + 1;
    // As we're adding to a set, this will automatically do a containment check
    generated.add(next);
}

Attenzione però alla scelta del set: l'ho usata deliberatamente LinkedHashSetperché mantiene l'ordine di inserzione, che qui ci interessa.

Un'altra opzione è fare sempre progressi, riducendo ogni volta l'intervallo e compensando i valori esistenti. Quindi, ad esempio, supponiamo di volere 3 valori nell'intervallo 0..9. Alla prima iterazione genereresti qualsiasi numero compreso tra 0 e 9, diciamo che generi un 4.

Nella seconda iterazione dovresti quindi generare un numero compreso tra 0 e 8. Se il numero generato è inferiore a 4, lo manterrai così com'è ... altrimenti ne aggiungerai uno. Questo ti dà un intervallo di risultati di 0..9 senza 4. Supponiamo di ottenere 7 in questo modo.

Alla terza iterazione genereresti un numero compreso tra 0 e 7. Se il numero generato è inferiore a 4, lo manterrai così com'è. Se è 4 o 5, ne aggiungerai uno. Se è 6 o 7, aggiungi due. In questo modo l'intervallo di risultati è 0..9 senza 4 o 6.


Genera un array dei valori possibili, selezionane uno a caso (dimensione array mod numero casuale), rimuovi (e salva) il numero selezionato, quindi ripeti.
Hot Licks

Oppure usa un generatore casuale con un ciclo completo (quelli basati su numeri primi possono usare numeri primi piccoli - con corrispondenti piccoli cicli) e rilascia valori fuori intervallo.
Paul de Vrieze

"Ancora un'altra opzione è fare sempre progressi" è WAAAAY una soluzione migliore. Si prega di modificare per riflettere. E grazie per questa fantastica risposta.
user123321

1
@musselwhizzle: Cercherò di trovare il tempo presto. Non sono sicuro di "WAAAY migliore" però - sarà significativamente meno "ovviamente corretto" anche se sarà più efficiente. Molto spesso sono felice di sacrificare le prestazioni per motivi di leggibilità.
Jon Skeet

@ Deepthi: Qualunque sia il massimo che l'OP vuole - come da domanda.
Jon Skeet

19

Ecco come lo farei

import java.util.ArrayList;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        int size = 20;

        ArrayList<Integer> list = new ArrayList<Integer>(size);
        for(int i = 1; i <= size; i++) {
            list.add(i);
        }

        Random rand = new Random();
        while(list.size() > 0) {
            int index = rand.nextInt(list.size());
            System.out.println("Selected: "+list.remove(index));
        }
    }
}

Come ha sottolineato lo stimato Mr Skeet:
se n è il numero di numeri selezionati casualmente che si desidera scegliere e N è lo spazio campionario totale dei numeri disponibili per la selezione:

  1. Se n << N , dovresti solo memorizzare i numeri che hai scelto e controllare un elenco per vedere se il numero selezionato è in esso.
  2. Se n ~ = N , dovresti probabilmente usare il mio metodo, compilando un elenco contenente l'intero spazio campione e quindi rimuovendo i numeri da esso mentre li selezioni.

la lista dovrebbe essere una LinkedList, rimuovere gli indici casuali dall'arraylist è molto inefficiente
Riccardo Casatta

@RiccardoCasatta hai una fonte per la tua affermazione? Non riesco nemmeno a immaginare che attraversare una lista collegata sarebbe molto performante. Vedi anche: stackoverflow.com/a/6103075/79450
Catchwa

L'ho provato e hai ragione, devo cancellare il mio commento?
Riccardo Casatta

@RiccardoCasatta Altri potrebbero trovare utile il nostro avanti e indietro
Catchwa

13
//random numbers are 0,1,2,3 
ArrayList<Integer> numbers = new ArrayList<Integer>();   
Random randomGenerator = new Random();
while (numbers.size() < 4) {

    int random = randomGenerator .nextInt(4);
    if (!numbers.contains(random)) {
        numbers.add(random);
    }
}

Ciò avrebbe prestazioni orribili per grandi numeri. ArrayList.contains sta iterando l'elenco. Sarebbe molto più pulito invece avere un Set: non è necessario controllare se contiene, basta aggiungere e le prestazioni sarebbero migliori.
kfox

5

C'è un altro modo per eseguire numeri ordinati "casuali" con LFSR, dai un'occhiata a:

http://en.wikipedia.org/wiki/Linear_feedback_shift_register

con questa tecnica puoi ottenere il numero casuale ordinato per indice e assicurandoti che i valori non vengano duplicati.

Ma questi non sono numeri casuali Veri perché la generazione casuale è deterministica.

Tuttavia, a seconda dei casi, è possibile utilizzare questa tecnica riducendo la quantità di elaborazione sulla generazione di numeri casuali quando si utilizza lo shuffling.

Ecco un algoritmo LFSR in java, (l'ho portato da qualche parte che non ricordo):

public final class LFSR {
    private static final int M = 15;

    // hard-coded for 15-bits
    private static final int[] TAPS = {14, 15};

    private final boolean[] bits = new boolean[M + 1];

    public LFSR() {
        this((int)System.currentTimeMillis());
    }

    public LFSR(int seed) {
        for(int i = 0; i < M; i++) {
            bits[i] = (((1 << i) & seed) >>> i) == 1;
        }
    }

    /* generate a random int uniformly on the interval [-2^31 + 1, 2^31 - 1] */
    public short nextShort() {
        //printBits();

        // calculate the integer value from the registers
        short next = 0;
        for(int i = 0; i < M; i++) {
            next |= (bits[i] ? 1 : 0) << i;
        }

        // allow for zero without allowing for -2^31
        if (next < 0) next++;

        // calculate the last register from all the preceding
        bits[M] = false;
        for(int i = 0; i < TAPS.length; i++) {
            bits[M] ^= bits[M - TAPS[i]];
        }

        // shift all the registers
        for(int i = 0; i < M; i++) {
            bits[i] = bits[i + 1];
        }

        return next;
    }

    /** returns random double uniformly over [0, 1) */
    public double nextDouble() {
        return ((nextShort() / (Integer.MAX_VALUE + 1.0)) + 1.0) / 2.0;
    }

    /** returns random boolean */
    public boolean nextBoolean() {
        return nextShort() >= 0;
    }

    public void printBits() {
        System.out.print(bits[M] ? 1 : 0);
        System.out.print(" -> ");
        for(int i = M - 1; i >= 0; i--) {
            System.out.print(bits[i] ? 1 : 0);
        }
        System.out.println();
    }


    public static void main(String[] args) {
        LFSR rng = new LFSR();
        Vector<Short> vec = new Vector<Short>();
        for(int i = 0; i <= 32766; i++) {
            short next = rng.nextShort();
            // just testing/asserting to make 
            // sure the number doesn't repeat on a given list
            if (vec.contains(next))
                throw new RuntimeException("Index repeat: " + i);
            vec.add(next);
            System.out.println(next);
        }
    }
}

4

Un altro approccio che consente di specificare quanti numeri si vuole con sizee l' mine maxvalori dei numeri restituiti

public static int getRandomInt(int min, int max) {
    Random random = new Random();

    return random.nextInt((max - min) + 1) + min;
}

public static ArrayList<Integer> getRandomNonRepeatingIntegers(int size, int min,
        int max) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();

    while (numbers.size() < size) {
        int random = getRandomInt(min, max);

        if (!numbers.contains(random)) {
            numbers.add(random);
        }
    }

    return numbers;
}

Per usarlo restituendo 7 numeri compresi tra 0 e 25.

    ArrayList<Integer> list = getRandomNonRepeatingIntegers(7, 0, 25);
    for (int i = 0; i < list.size(); i++) {
        System.out.println("" + list.get(i));
    }

4

Questo sarebbe molto più semplice in java-8:

Stream.generate(new Random()::ints)
            .distinct()
            .limit(16) // whatever limit you might need
            .toArray(Integer[]::new);

3

Il modo più efficiente e semplice per avere numeri casuali non ripetitivi è spiegato da questo pseudo-codice. Non è necessario disporre di cicli nidificati o ricerche con hash:

// get 5 unique random numbers, possible values 0 - 19
// (assume desired number of selections < number of choices)

const int POOL_SIZE = 20;
const int VAL_COUNT = 5;

declare Array mapping[POOL_SIZE];
declare Array results[VAL_COUNT];

declare i int;
declare r int;
declare max_rand int;

// create mapping array
for (i=0; i<POOL_SIZE; i++) {
   mapping[i] = i;
}

max_rand = POOL_SIZE-1;  // start loop searching for maximum value (19)

for (i=0; i<VAL_COUNT; i++) {
    r = Random(0, max_rand); // get random number
    results[i] = mapping[r]; // grab number from map array
    mapping[r] = max_rand;  // place item past range at selected location

    max_rand = max_rand - 1;  // reduce random scope by 1
}

Supponiamo che la prima iterazione abbia generato il numero casuale 3 per iniziare (da 0 a 19). Ciò produrrebbe risultati [0] = mapping [3], ovvero il valore 3. Assegneremo quindi mapping [3] a 19.

Nella successiva iterazione, il numero casuale era 5 (da 0 a 18). Ciò produrrebbe risultati [1] = mapping [5], ovvero il valore 5. Assegneremo quindi mapping [5] a 18.

Supponiamo ora che la successiva iterazione scelga di nuovo 3 (da 0 a 17). ai risultati [2] verrebbe assegnato il valore di mappatura [3], ma ora questo valore non è 3, ma 19.

Questa stessa protezione persiste per tutti i numeri, anche se hai ottenuto lo stesso numero 5 volte di seguito. Ad esempio, se il generatore di numeri casuali ti ha dato 0 cinque volte di seguito, i risultati sarebbero: [0, 19, 18, 17, 16].

Non otterresti mai lo stesso numero due volte.


Dubito che questo sia casuale come lo fai sembrare. Supera i test di casualità standard ?; sembrerebbe concentrare i numeri vicino alla fine dello spettro.
tucuxi

Ecco un caso di base. La piscina è {a, b, c}. Abbiamo bisogno di 2 elementi non ripetitivi. Seguendo l'algoritmo, ecco le combinazioni che potremmo disegnare ei loro risultati: 0,0: a, c 0,1: a, b 1,0: b, a 1,1: b, c 2,0: c, a 2, 1: c, b Punteggio: a-4, b-4, c-4
blackcatweb

3

Generare tutti gli indici di una sequenza è generalmente una cattiva idea, in quanto potrebbe richiedere molto tempo, soprattutto se il rapporto tra i numeri da scegliere MAXè basso (la complessità diventa dominata da O(MAX)). Ciò peggiora se il rapporto tra i numeri da scegliere si MAXavvicina a uno, poiché anche rimuovere gli indici scelti dalla sequenza di tutti diventa costoso (ci avviciniamo O(MAX^2/2)). Ma per piccoli numeri, questo generalmente funziona bene e non è particolarmente soggetto a errori.

Anche filtrare gli indici generati utilizzando una raccolta è una cattiva idea, poiché un po 'di tempo viene speso per inserire gli indici nella sequenza e il progresso non è garantito poiché lo stesso numero casuale può essere estratto più volte (ma per abbastanza grande MAXè improbabile ). Questo potrebbe essere vicino alla complessità
O(k n log^2(n)/2), ignorando i duplicati e assumendo che la raccolta utilizzi un albero per una ricerca efficiente (ma con un costo costante significativo per l' kallocazione dei nodi dell'albero e possibilmente dover ribilanciare ).

Un'altra opzione è generare i valori casuali in modo univoco dall'inizio, garantendo il progresso. Ciò significa che nel primo round [0, MAX]viene generato un indice casuale in :

items i0 i1 i2 i3 i4 i5 i6 (total 7 items)
idx 0       ^^             (index 2)

Nel secondo round, [0, MAX - 1]viene generato solo (poiché un elemento era già selezionato):

items i0 i1    i3 i4 i5 i6 (total 6 items)
idx 1          ^^          (index 2 out of these 6, but 3 out of the original 7)

I valori degli indici devono quindi essere adeguati: se il secondo indice cade nella seconda metà della sequenza (dopo il primo indice), deve essere incrementato per tenere conto del divario. Possiamo implementarlo come un ciclo, permettendoci di selezionare un numero arbitrario di elementi unici.

Per sequenze brevi, questo è un O(n^2/2)algoritmo abbastanza veloce :

void RandomUniqueSequence(std::vector<int> &rand_num,
    const size_t n_select_num, const size_t n_item_num)
{
    assert(n_select_num <= n_item_num);

    rand_num.clear(); // !!

    // b1: 3187.000 msec (the fastest)
    // b2: 3734.000 msec
    for(size_t i = 0; i < n_select_num; ++ i) {
        int n = n_Rand(n_item_num - i - 1);
        // get a random number

        size_t n_where = i;
        for(size_t j = 0; j < i; ++ j) {
            if(n + j < rand_num[j]) {
                n_where = j;
                break;
            }
        }
        // see where it should be inserted

        rand_num.insert(rand_num.begin() + n_where, 1, n + n_where);
        // insert it in the list, maintain a sorted sequence
    }
    // tier 1 - use comparison with offset instead of increment
}

Dov'è il n_select_numtuo 5 ed n_number_numè il tuo MAX. I n_Rand(x)rendimenti interi casuali a [0, x](compreso). Questo può essere reso un po 'più veloce se si selezionano molti elementi (es. Non 5 ma 500) utilizzando la ricerca binaria per trovare il punto di inserimento. Per farlo, dobbiamo assicurarci di soddisfare i requisiti.

Faremo una ricerca binaria con il confronto n + j < rand_num[j]che è lo stesso di
n < rand_num[j] - j. Dobbiamo dimostrare che rand_num[j] - jè ancora una sequenza ordinata per una sequenza ordinata rand_num[j]. Questo è fortunatamente facilmente mostrato, poiché la distanza più bassa tra due elementi dell'originale rand_numè uno (i numeri generati sono unici, quindi c'è sempre una differenza di almeno 1). Allo stesso tempo, se sottraiamo gli indici jda tutti gli elementi
rand_num[j], le differenze nell'indice sono esattamente 1. Quindi, nel caso "peggiore", otteniamo una sequenza costante, ma mai decrescente. La ricerca binaria può quindi essere utilizzata, producendo O(n log(n))algoritmo:

struct TNeedle { // in the comparison operator we need to make clear which argument is the needle and which is already in the list; we do that using the type system.
    int n;

    TNeedle(int _n)
        :n(_n)
    {}
};

class CCompareWithOffset { // custom comparison "n < rand_num[j] - j"
protected:
    std::vector<int>::iterator m_p_begin_it;

public:
    CCompareWithOffset(std::vector<int>::iterator p_begin_it)
        :m_p_begin_it(p_begin_it)
    {}

    bool operator ()(const int &r_value, TNeedle n) const
    {
        size_t n_index = &r_value - &*m_p_begin_it;
        // calculate index in the array

        return r_value < n.n + n_index; // or r_value - n_index < n.n
    }

    bool operator ()(TNeedle n, const int &r_value) const
    {
        size_t n_index = &r_value - &*m_p_begin_it;
        // calculate index in the array

        return n.n + n_index < r_value; // or n.n < r_value - n_index
    }
};

E infine:

void RandomUniqueSequence(std::vector<int> &rand_num,
    const size_t n_select_num, const size_t n_item_num)
{
    assert(n_select_num <= n_item_num);

    rand_num.clear(); // !!

    // b1: 3578.000 msec
    // b2: 1703.000 msec (the fastest)
    for(size_t i = 0; i < n_select_num; ++ i) {
        int n = n_Rand(n_item_num - i - 1);
        // get a random number

        std::vector<int>::iterator p_where_it = std::upper_bound(rand_num.begin(), rand_num.end(),
            TNeedle(n), CCompareWithOffset(rand_num.begin()));
        // see where it should be inserted

        rand_num.insert(p_where_it, 1, n + p_where_it - rand_num.begin());
        // insert it in the list, maintain a sorted sequence
    }
    // tier 4 - use binary search
}

L'ho testato su tre benchmark. Innanzitutto, sono stati scelti 3 numeri su 7 elementi e un istogramma degli elementi scelti è stato accumulato su 10.000 esecuzioni:

4265 4229 4351 4267 4267 4364 4257

Ciò mostra che ciascuno dei 7 elementi è stato scelto approssimativamente lo stesso numero di volte e non vi è alcun pregiudizio apparente causato dall'algoritmo. È stata inoltre verificata la correttezza di tutte le sequenze (unicità dei contenuti).

Il secondo benchmark prevedeva la scelta di 7 numeri su 5000 articoli. Il tempo di diverse versioni dell'algoritmo è stato accumulato su 10.000.000 di esecuzioni. I risultati sono indicati nei commenti nel codice come b1. La versione semplice dell'algoritmo è leggermente più veloce.

Il terzo benchmark prevedeva la scelta di 700 numeri su 5000 articoli. Il tempo di diverse versioni dell'algoritmo è stato nuovamente accumulato, questa volta oltre 10.000 esecuzioni. I risultati sono indicati nei commenti nel codice come b2. La versione di ricerca binaria dell'algoritmo è ora più di due volte più veloce di quella semplice.

Il secondo metodo inizia ad essere più veloce per la scelta di più di circa 75 elementi sulla mia macchina (nota che la complessità di entrambi gli algoritmi non dipende dal numero di elementi MAX).

Vale la pena ricordare che gli algoritmi di cui sopra generano i numeri casuali in ordine crescente. Ma sarebbe semplice aggiungere un altro array in cui i numeri sarebbero salvati nell'ordine in cui sono stati generati e restituirlo invece (a un costo aggiuntivo trascurabile O(n)). Non è necessario mescolare l'output: sarebbe molto più lento.

Nota che i sorgenti sono in C ++, non ho Java sulla mia macchina, ma il concetto dovrebbe essere chiaro.

MODIFICA :

Per divertimento, ho anche implementato l'approccio che genera una lista con tutti gli indici
0 .. MAX, li sceglie a caso e li rimuove dalla lista per garantire l'unicità. Dato che ho scelto abbastanza alto MAX(5000), le prestazioni sono catastrofiche:

// b1: 519515.000 msec
// b2: 20312.000 msec
std::vector<int> all_numbers(n_item_num);
std::iota(all_numbers.begin(), all_numbers.end(), 0);
// generate all the numbers

for(size_t i = 0; i < n_number_num; ++ i) {
    assert(all_numbers.size() == n_item_num - i);
    int n = n_Rand(n_item_num - i - 1);
    // get a random number

    rand_num.push_back(all_numbers[n]); // put it in the output list
    all_numbers.erase(all_numbers.begin() + n); // erase it from the input
}
// generate random numbers

Ho anche implementato l'approccio con un set(una raccolta C ++), che in realtà arriva secondo nel benchmark b2, essendo solo circa il 50% più lento rispetto all'approccio con la ricerca binaria. Ciò è comprensibile, poiché setutilizza un albero binario, in cui il costo di inserimento è simile alla ricerca binaria. L'unica differenza è la possibilità di ottenere elementi duplicati, il che rallenta l'avanzamento.

// b1: 20250.000 msec
// b2: 2296.000 msec
std::set<int> numbers;
while(numbers.size() < n_number_num)
    numbers.insert(n_Rand(n_item_num - 1)); // might have duplicates here
// generate unique random numbers

rand_num.resize(numbers.size());
std::copy(numbers.begin(), numbers.end(), rand_num.begin());
// copy the numbers from a set to a vector

Il codice sorgente completo è qui .


2

È possibile utilizzare una delle classi che implementano l'interfaccia Set ( API ), quindi ogni numero generato, utilizzare Set.add () per inserirlo.

Se il valore restituito è falso, sai che il numero è già stato generato in precedenza.


2

Invece di fare tutto ciò, crea un LinkedHashSetoggetto e numeri casuali ad esso per Math.random()funzione .... se si verifica una voce duplicata, l' LinkedHashSetoggetto non aggiungerà quel numero alla sua lista ... Poiché in questa classe di raccolta non sono consentiti valori duplicati .. alla fine ottieni una lista di numeri casuali che non hanno valori duplicati ....: D


2

Il tuo problema sembra ridursi a scegliere k elementi a caso da una raccolta di n elementi. La risposta Collections.shuffle è quindi corretta, ma come sottolineato inefficiente: è O (n).

Wikipedia: Fisher – Yates shuffle ha una versione O (k) quando l'array esiste già. Nel tuo caso, non esiste un array di elementi e creare l'array di elementi potrebbe essere molto costoso, ad esempio se il massimo fosse 10000000 invece di 20.

L'algoritmo di shuffle implica l'inizializzazione di un array di dimensione n in cui ogni elemento è uguale al suo indice, scegliendo k numeri casuali per ogni numero in un intervallo con il massimo inferiore all'intervallo precedente, quindi scambiando gli elementi verso la fine dell'array.

Puoi fare la stessa operazione in O (k) time con una hashmap anche se ammetto che sia un tipo di dolore. Nota che questo vale solo se k è molto inferiore a n. (cioè k ~ lg (n) o giù di lì), altrimenti dovresti usare lo shuffle direttamente.

Userai la tua hashmap come una rappresentazione efficiente dell'array di supporto nell'algoritmo di shuffle. Qualsiasi elemento dell'array che è uguale al suo indice non deve necessariamente apparire nella mappa. Ciò consente di rappresentare un array di dimensione n in tempo costante, non c'è tempo per inizializzarlo.

  1. Scegli k numeri casuali: il primo è nell'intervallo da 0 a n-1, il secondo da 0 a n-2, il terzo da 0 a n-3 e così via, fino a nk.

  2. Considera i tuoi numeri casuali come un insieme di scambi. Il primo indice casuale passa alla posizione finale. Il secondo indice casuale passa alla penultima posizione. Tuttavia, invece di lavorare contro un array di supporto, lavora contro la tua hashmap. La tua hashmap memorizzerà ogni elemento che è fuori posizione.

int getValue(i) { if (map.contains(i)) return map[i]; return i; } void setValue(i, val) { if (i == val) map.remove(i); else map[i] = val; } int[] chooseK(int n, int k) { for (int i = 0; i < k; i++) { int randomIndex = nextRandom(0, n - i); //(n - i is exclusive) int desiredIndex = n-i-1; int valAtRandom = getValue(randomIndex); int valAtDesired = getValue(desiredIndex); setValue(desiredIndex, valAtRandom); setValue(randomIndex, valAtDesired); } int[] output = new int[k]; for (int i = 0; i < k; i++) { output[i] = (getValue(n-i-1)); } return output; }


creating the array of elements could be very expensive- perché la creazione di un array dovrebbe essere più costosa del mescolamento? Penso che non ci sia assolutamente motivo di pessimismo su questo punto :-)
Wolf

1

Il codice seguente crea un numero casuale di sequenza compreso tra [1, m] che non è stato generato prima.

public class NewClass {

    public List<Integer> keys = new ArrayList<Integer>();

    public int rand(int m) {
        int n = (int) (Math.random() * m + 1);
        if (!keys.contains(n)) {
            keys.add(n);
            return n;
        } else {
            return rand(m);
        }
    }

    public static void main(String[] args) {
        int m = 4;
        NewClass ne = new NewClass();
        for (int i = 0; i < 4; i++) {
            System.out.println(ne.rand(m));
        }
        System.out.println("list: " + ne.keys);
    }
}

0

Esiste un algoritmo per il lotto di carte: si crea un array ordinato di numeri (il "lotto di carte") e in ogni iterazione si seleziona un numero in una posizione casuale da esso (rimuovendo il numero selezionato dal "lotto di carte" ovviamente).


0

Ecco una soluzione efficiente per la creazione rapida di un array randomizzato. Dopo la randomizzazione puoi semplicemente scegliere il n-esimo elemento edell'array, incrementare ne tornare e. Questa soluzione ha O (1) per ottenere un numero casuale e O (n) per l'inizializzazione, ma come compromesso richiede una buona quantità di memoria se n diventa abbastanza grande.


0

Esiste una soluzione più efficiente e meno ingombrante per i numeri interi rispetto a Collections.shuffle.

Il problema è lo stesso del prelievo successivo degli articoli solo da quelli non selezionati in un set e del loro ordine da qualche altra parte. È esattamente come distribuire carte a caso o estrarre biglietti della lotteria vincenti da un cappello o da un bidone.

Questo algoritmo funziona per caricare qualsiasi array e ottenere un ordine casuale alla fine del caricamento. Funziona anche per l'aggiunta in una raccolta List (o qualsiasi altra raccolta indicizzata) e per ottenere una sequenza casuale nella raccolta alla fine delle aggiunte.

Può essere fatto con un singolo array, creato una volta o con una collection ordinata numericamente, come un List, in posizione. Per un array, la dimensione iniziale dell'array deve essere la dimensione esatta per contenere tutti i valori desiderati. Se non sai quanti valori potrebbero verificarsi in anticipo, funzionerà anche usando una raccolta ordinata numericamente, come un ArrayList o List, dove la dimensione non è immutabile. Funzionerà universalmente per un array di qualsiasi dimensione fino a Integer.MAX_VALUE che è poco più di 2.000.000.000. Gli oggetti elenco avranno gli stessi limiti di indice. La tua macchina potrebbe esaurire la memoria prima di arrivare a un array di quelle dimensioni. Potrebbe essere più efficiente caricare un array digitato nei tipi di oggetto e convertirlo in una raccolta, dopo aver caricato l'array. Ciò è particolarmente vero se la raccolta di destinazione non è indicizzata numericamente.

Questo algoritmo, esattamente come scritto, creerà una distribuzione molto uniforme in cui non ci sono duplicati. Un aspetto MOLTO IMPORTANTE è che deve essere possibile che l'inserimento dell'elemento successivo avvenga fino alla dimensione corrente + 1. Quindi, per il secondo articolo, potrebbe essere possibile memorizzarlo nella posizione 0 o nella posizione 1 Per il 20 ° oggetto, potrebbe essere possibile memorizzarlo in qualsiasi posizione, da 0 a 19. È altrettanto possibile che il primo oggetto rimanga nella posizione 0 in quanto è possibile che finisca in qualsiasi altra posizione. È altrettanto possibile che il nuovo elemento successivo vada ovunque, inclusa la nuova posizione successiva.

La casualità della sequenza sarà casuale quanto la casualità del generatore di numeri casuali.

Questo algoritmo può essere utilizzato anche per caricare i tipi di riferimento in posizioni casuali in un array. Poiché funziona con un array, può funzionare anche con le raccolte. Ciò significa che non è necessario creare la raccolta e quindi mescolarla o ordinarla su qualsiasi ordine gli oggetti vengano inseriti. La collezione deve solo avere la possibilità di inserire un elemento in un punto qualsiasi della collezione o di aggiungerlo.

// RandomSequence.java
import java.util.Random;
public class RandomSequence {

    public static void main(String[] args) {
        // create an array of the size and type for which
        // you want a random sequence
        int[] randomSequence = new int[20];
        Random randomNumbers = new Random();

        for (int i = 0; i < randomSequence.length; i++ ) {
            if (i == 0) { // seed first entry in array with item 0
                randomSequence[i] = 0; 
            } else { // for all other items...
                // choose a random pointer to the segment of the
                // array already containing items
                int pointer = randomNumbers.nextInt(i + 1);
                randomSequence[i] = randomSequence[pointer]; 
                randomSequence[pointer] = i;
                // note that if pointer & i are equal
                // the new value will just go into location i and possibly stay there
                // this is VERY IMPORTANT to ensure the sequence is really random
                // and not biased
            } // end if...else
        } // end for
        for (int number: randomSequence) {
                System.out.printf("%2d ", number);
        } // end for
    } // end main
} // end class RandomSequence

0

Dipende tutto esattamente da PER cosa hai bisogno della generazione casuale, ma ecco la mia opinione.

Innanzitutto, crea un metodo autonomo per generare il numero casuale. Assicurati di consentire i limiti.

public static int newRandom(int limit){
    return generatedRandom.nextInt(limit);  }

Successivamente, vorrai creare una struttura decisionale molto semplice che confronti i valori. Questo può essere fatto in due modi. Se hai un numero molto limitato di numeri da verificare, sarà sufficiente una semplice istruzione IF:

public static int testDuplicates(int int1, int int2, int int3, int int4, int int5){
    boolean loopFlag = true;
    while(loopFlag == true){
        if(int1 == int2 || int1 == int3 || int1 == int4 || int1 == int5 || int1 == 0){
            int1 = newRandom(75);
            loopFlag = true;    }
        else{
            loopFlag = false;   }}
    return int1;    }

Quanto sopra confronta int1 con int2 fino a int5, oltre a garantire che non ci siano zeri nei randoms.

Con questi due metodi in atto, possiamo fare quanto segue:

    num1 = newRandom(limit1);
    num2 = newRandom(limit1);
    num3 = newRandom(limit1);
    num4 = newRandom(limit1);
    num5 = newRandom(limit1);

Seguito da:

        num1 = testDuplicates(num1, num2, num3, num4, num5);
        num2 = testDuplicates(num2, num1, num3, num4, num5);
        num3 = testDuplicates(num3, num1, num2, num4, num5);
        num4 = testDuplicates(num4, num1, num2, num3, num5);
        num5 = testDuplicates(num5, num1, num2, num3, num5);

Se hai un elenco più lungo da verificare, un metodo più complesso produrrà risultati migliori sia nella chiarezza del codice che nelle risorse di elaborazione.

Spero che sia di aiuto. Questo sito mi ha aiutato così tanto, mi sono sentito obbligato almeno a PROVARE ad aiutare.



0

Il modo più semplice è utilizzare nano DateTime come formato lungo. System.nanoTime ();

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.