2012-02-18 7 views
16

Ho un vettore contenente elementi n. Devo scegliere un sottoinsieme di elementi m in modo casuale dal vettore senza ripetizione. Qual è il modo più efficace per farlo? Ho bisogno di farlo diverse migliaia di volte nel mio codice.Scegli m elementi in modo casuale da un vettore contenente n elementi

La soluzione in cima alla mia mente è quello di utilizzare rand() per generare un numero casuale tra k0 e n. Quindi seleziona l'elemento nel vettore e inseriscilo in un std::set. Continuate a farlo fino a quando le dimensioni del set diventano uguali a m. Ora ho la certezza che il set contiene m elementi unici scelti casualmente dal set di elementi n.

Quali sono le altre soluzioni possibili?

Grazie.

+4

Fare 'std: : random_shuffle() 'sul vettore e tira fuori i primi elementi' m', forse? – jrok

+0

@jrok: mentre semplice, è _ notevolmente inefficiente quando 'm' è molto più piccolo di' n'. –

+0

possibile duplicato di [Algoritmo per selezionare una singola combinazione casuale di valori?] (Http://stackoverflow.com/questions/2394246/algorithm-to-select-a-single-random-combination-of-values) –

risposta

29

Volete un Fisher-Yates shuffle (fermata dopo iterazioni M):

template<class BidiIter > 
BidiIter random_unique(BidiIter begin, BidiIter end, size_t num_random) { 
    size_t left = std::distance(begin, end); 
    while (num_random--) { 
     BidiIter r = begin; 
     std::advance(r, rand()%left); 
     std::swap(*begin, *r); 
     ++begin; 
     --left; 
    } 
    return begin; 
} 

Demo a http://ideone.com/3A3cv. Questo è significativamente più veloce di std::random_shuffle quando sono necessari solo pochi numeri casuali dal set e dovrebbe essere quasi della stessa velocità anche se N==M.

+0

@ Grazie Burr! Ho un milione di elementi nel mio vettore di cui devo scegliere solo 100 elementi in ordine casuale. Questo e 'esattamente quello che stavo cercando. – Vinay

+0

Grazie per il codice! Funziona perfettamente. – Danvil

+2

rand(): vedere http://codereview.stackexchange.com/questions/39001/fisher-yates-modern-shuffle-algorithm – dani

3

Un modo che si possa fare è quello di creare una lista di tutti gli indici del vettore, mescolarle, e prendere la prima n di essere gli indici degli oggetti selezionati:

struct rangegenerator { 
    rangegenerator(int init) : start(init) { } 

    int operator()() { 
     return start++; 
    } 

    int start; 
}; 

vector<T> numbers; // this is filled somewhere else 

vector<int> indices(numbers.size()); 

generate(begin(indices), end(indices), rangegenerator(0)); 

random_shuffle(begin(indices), end(indices)); 

// then take the first n elements of indices and use them as indices into numbers 
+3

Quando 'm' è molto più piccolo di' n', questo è altamente inefficiente. Non è difficile trovare una risposta che sia più veloce di questa per tutti 'm' (dove' m' è minore di 'n') –

+0

@Seth: Dovrà essere d'accordo con Moo. Questo è probabilmente uno dei peggiori modi per portare a termine il compito dato - non sono sicuro del motivo per cui l'OP lo ha contrassegnato come una risposta. La risposta corretta è ovviamente la risposta di Burr. –

+1

@JaredKrumsie l'OP ha chiesto "altre possibili soluzioni" e ciò che ho scritto è sicuramente una possibile soluzione. L'unico modo in cui una risposta sarebbe errata è se non ha funzionato affatto. –

Problemi correlati