2014-11-19 23 views
13

definisco un unordered_map come questo:Selezionare elemento casuale in un unordered_map

std::unordered_map<std::string, Edge> edges; 

C'è un modo efficace per scegliere un bordo a caso dai bordi unordered_map?

+0

Che cosa stai usando adesso? E hai profilato che questo metodo è un collo di bottiglia? – Borgleader

+0

possibile duplicato di [Elemento casuale in una mappa] (http://stackoverflow.com/questions/158836/random-element-in-a-map) – Chnossos

risposta

5

Questo è come è possibile ottenere elemento casuale da una mappa:

std::unordered_map<std::string, Edge> edges; 
iterator item = edges.begin(); 
int random_index = rand() % edges.size(); 
std::advance(item, random_index); 

Oppure dare un'occhiata a this answer, che fornisce la seguente soluzione:

std::unordered_map<std::string, Edge> edges; 
iterator item = edges.begin(); 
std::advance(item, random_0_to_n(edges.size())); 
18

Pre-C++ soluzione 11 :

std::tr1::unordered_map<std::string, Edge> edges; 
std::tr1::unordered_map<std::string, Edge>::iterator random_it = edges.begin(); 
std::advance(random_it, rand_between(0, edges.size())); 

C++ 11 in poi soluzione:

std::unordered_map<std::string, Edge> edges; 
auto random_it = std::next(std::begin(edges), rand_between(0, edges.size())); 

La funzione che seleziona un numero casuale valida è fino a vostra scelta, ma essere sicuri che restituisce un numero nella gamma [0 ; edges.size() - 1] quando edges non è vuoto.

La funzione std::next avvolge semplicemente la funzione std::advance in un modo che consente l'assegnazione diretta.

+0

Ricordare 'rand_between (0, edges.size())' potrebbe restituire 'edges.size()' e 'random_it' punterà su' edges.end() '. Suggerirei 'rand_between (0, edges.size() - 1)' ma dobbiamo essere certi che 'edges' non sia vuoto. –

+0

Sì, deve essere nell'intervallo '[0, edge.size() ['. – Chnossos

+0

Quale sarebbe la complessità temporale della seconda soluzione? È necessario eseguire in modo indipendente n chiavi casuali per recuperare l'ennesima chiave casuale? – blueman

8

Esiste un modo efficace per scegliere un Edge casuale dai bordi unordered_map?

Se per efficiente si intende O (1), quindi no, non è possibile.

Poiché gli iteratori restituiti da unordered_map::begin/end sono ForwardIterator s, gli approcci che utilizzano semplicemente std::advance sono O (n) nel numero di elementi.

Se l'uso specifico lo consente, è possibile barattare un po 'di casualità per l'efficienza:

È possibile selezionare un casuale secchio (cui si può accedere in O (1)), e poi un elemento casuale all'interno di quella secchio.

int bucket, bucket_size; 
do 
{ 
    bucket = rnd(edges.bucket_count()); 
} 
while ((bucket_size = edges.bucket_size(bucket)) == 0); 

auto element = std::next(edges.begin(bucket), rnd(bucket_size)); 

Dove rnd(n) restituisce un numero casuale nella 0, n range [).

In pratica, se si dispone di un hash decente, la maggior parte dei bucket conterrà esattamente un elemento, altrimenti questa funzione privilegerà leggermente gli elementi che sono soli nei rispettivi bucket.

+0

Questo è un metodo elegante! Tuttavia, c'è un piccolo problema: 'std :: advance' non restituisce un iteratore, invece, dovresti usare' std :: next'. –

+0

fisso @for_stack, grazie – sbabbi

+0

Con quel ciclo while non c'è alcuna garanzia che il tuo metodo restituirà qualcosa. Probabilmente non ci si imbatterà in questo, ma se la mappa non è densamente popolata, potrebbe trasformarsi in un problema. –

1

Strict O (1) soluzione senza secchi:

  1. Tenere un vettore di chiavi, quando si ha bisogno di ottenere un elemento casuale dalla mappa, selezionare una chiave casuale dal vettore e restituire valore corrispondente dal mappa - richiede tempo costante
  2. Se inserisci una coppia chiave-valore nella tua mappa, controlla se tale chiave è già presente, e se non è così, aggiungi quella chiave al tuo vettore chiave - richiede tempo costante
  3. Se vuoi rimuovere un elemento dalla mappa dopo che è stato selezionato, scambiare la chiave selezionata con l'elemento back() del tuo vettore chiave e chiamare pop_ indietro(), dopo che cancella l'elemento dalla mappa e restituisce il valore - richiede tempo costante

Tuttavia, vi è una limitazione: se si desidera eliminare elementi dalla mappa oltre al prelievo casuale, è necessario correggi il tuo vettore chiave, questo prende O (n) con un approccio ingenuo. Ma c'è ancora un modo per ottenere prestazioni O (1): tieni una mappa che ti dice dove si trova la chiave nel vettore chiave e aggiornala con lo scambio :)

Problemi correlati