|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
[C++] hashset e liste di collisione
Salve a tutti ^^
Avrei un dubbio, diciamo teorico, sull'implementazione di un hashset con gestione delle collisioni mediante liste. E' un progetto universitario abbastanza semplice ma ho cmq qualche dubbio. Gestire le collisioni con liste vuol dire che (ad esempio), il mio hashset è implementato mediante un array di coppie hash-lista, o che le coppie sono hash-valore e l'hashset contiene una lista in cui infilo i dati che generano hash uguale a quelli gia presenti? O ancora dovrei creare un'array di tuple formate da hash-valore-lista? se si, quando avviene una collisione il valore viene sostituito e quindi quello vecchio viene aggiunto alla lista? Sono perplesso XD Spero di essere stato chiaro ^^ grazie per l'attenzione Saluti
__________________
iMac 27" 5K |
|
|
|
|
|
#2 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Le coppie sono Hash lista. In ogni lista ci sono tutti i valori con lo stesso hash.
|
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
Quindi se volessi iterare sul mio hash set, l'iteratore punterebbe a cosa? alla coppia hash-lista, o solo alla lista?
Grazie per la risposta Saluti
__________________
iMac 27" 5K |
|
|
|
|
|
#4 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
In C++ non esiste il contenitore hash set. Dipende tutto da come implementi l'hash set, se lo implementi con una map l'iteratore punterà alla coppia.
|
|
|
|
|
|
#5 |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
Si si lo sò che non c'è ^^ difatti il progetto ne prevede l'implementazione
Io l'ho implementato in due modi differenti: -array di pair<hash, lista>: facile da gestire (non deve nemmeno essere ridimensionabile), con iteratiori che puntano alla sola lista (ho pensato che essendo un "set", non avrebbe senso tornare la coppia, ma probabilmente ho pensato una vaccata) -lista lincata di struct fatte così: Codice:
struct tupla {
tupla* prec;
tupla* next;
int hash;
lista* list;
...ecc ecc...
};
Però se mi dici che dipende da come implemento l'hashset allora vuol dire che sono buone tutte le soluzioni...giusto? Arigrazie Ciauz
__________________
iMac 27" 5K |
|
|
|
|
|
#6 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Imho prima di tutto devi fare una classe lista. Quando ottieni un oggetto di classe lista ti basta fare:
list.add(element); Sia set che map della STL lavorano sui pair, quindi ha senso che un iteratore ritorni un pair. Mi immagino che tu non possa usare un contenitore della libreria standard per contenere i vari hash, anche perché a seconda di come hai costruito l'hash si possono sfruttare vari metodi di accesso diretto. Mettiamo che il tuo hash sia a 24 bit. Una struttura di 16 MB è perfettamente gestibile da qualsiasi computer. In questo modo hai la possibilità di accedere con complessità O(1) alle liste. Racchiuderei il vettore di un'altra classe hash_map (di fatto è un map, perché è ordinato secondo la chiave). Fare un hash a 32 bit alla fine ha veramente poco senso, a quel punto ti conviene davvero utilizzare un contenitore associativo della libreria standard, perché non potresti sfruttare l'accesso O(1). |
|
|
|
|
|
#7 | |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
Si si lista ce la fornisce il prof
Altra cosa non mi è chiara...se io implemento l'hashset con un array di pair<hash, lista>, come fò ad ottenere O(1) in accesso??? In un ipotetico metodo find, pensavo di calcolare l'hash del dato passato come parametro e di scorrere l'array finchè non trovo l'hash uguale, ma non credo affato che sia O(1) fare così... Quote:
Sei troppo iper gentile ^_^ Grazie per la risposta
__________________
iMac 27" 5K |
|
|
|
|
|
|
#8 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Non esiste un primitivo a 24 bit, ma puoi limitare il range dell'hash fino a 2^24.
Se fai un vettore di questo tipo: lista hashtable = new lista[16777216]; //16777216 è 2^24 Puoi ottenere l'accesso O(1) alla lista con un determinato hash. Per verificare se l'elemento esiste: hashtable[hash(elemento_da_cercare)].find(elemento_da_cercare) Il prezzo da pagare è nell'allocazione di memoria. Infatti in questo modo bisogna allocare preventivamente tanti elementi lista quanto è la dimensione del dominio dell'hash. Quindi la funzione hash() che ho scritto sopra deve ritornare un intero fra 0 e 2^24. Chiaramente puoi anche scendere di dimensione, ad esempio 16 bit, così puoi "sprecare" meno memoria. Volendo puoi anche semplicemente dichiarare un vettore di puntatori a lista, risparmiando ancora più memoria se lista occupa molto spazio. In questo modo dovrai inizializzarli a null. Se un puntatore è null significa che none sistono elementi con quell'hash. |
|
|
|
|
|
#9 |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
aaaaaaaah ok ci sono
Quindi se il mio hash, ad esempio, sta nel dominio 0-500, mi alloco subito un array che ricopre il dominio, così da poter usare l'hash come indice E se invece, alloco un'array di dimensione, mettiamo 50, e dopo aver fatto l'hash ne faccio il modulo di 50? così da ottenere un indice compreso non nel dominio dell'hash ma nella dimensione dell'array? Però adesso che ci penso non sò a priori quanto sia il dominio dell'hash...la funzione di hashing infatti può essere passata come funtore del mio hashset in fase di costruzione, quindi teoricamente potrebbe anche tornarmi qualcosa come 4294967296 (2^32) visto che l'unico limite che impongo è che l'hash sia un int (forse sarebbe meglio un unsigned int). Cmq grazie per la risposta, ora mi è chiaro l'accesso in O(1)
__________________
iMac 27" 5K |
|
|
|
|
|
#10 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Certo, se fai il modulo va bene, ma di fatto fai un altro hash
|
|
|
|
|
|
#11 |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
Vero
Quindi per risparmiare memoria a gogo mi consigli di: -rendere l'hash il più piccolo possibile (16 o 24 bit) -creare un array di pair<hash, lista*> con lista* = 0 se non ci sono elementi con quell'hash Sei er mejo Ma se volessi usare delle liste lincate invece che un array, come potrei ottenere O(1) in accesso? Riririririgrazie Ciauz
__________________
iMac 27" 5K |
|
|
|
|
|
#12 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Ovviamente no. La ricerca in O(1) è dovuta proprio al vettore.
A questo punto il pair non ti serve. |
|
|
|
|
|
#13 |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
ok tutto chiaro ^^ grazie mille ancora
__________________
iMac 27" 5K |
|
|
|
|
|
#14 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Se l'elenco non viene riempito con una densità costante, ma ha delle "bolle" di NULL e di hashset pieni, potresti risparmiare memoria allocando l'array "a pezzi" invece che tutto insieme, con una specie di struttura ad albero.
Ma alla fine 16 mb non sono niente quindi mi pare eccessivo
|
|
|
|
|
|
#15 | |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Quote:
Ad esempio, mettiamo che tu faccia una partizione del vettore che riguarda gli 8 bit più significativi. La tabella principale contiene 2^8 elementi: lista **hashtable[256]; devi impostare a NULL ogni elemento del vettore. A questo punto ogni volta che vuoi inserire un elemento nell'hash ottieni gli 8 bit più significativi dell'hash, recuperi il valaore di hashtable corrispondente, se è NULL significa che non ci sono elementi: Codice:
if(hashtable[hash >> 16] == NULL)
{
hashtable[hash >> 16] = new lista*[65536];
}
if(hashtable[hash >> 16][hash & 0x0FFFF] == NULL)
{
hashtable[hash >> 16][hash & 0x0FFFF] = new lista();
}
hashtable[hash >> 16][hash & 0x0FFFF].add(nuovo_elemento);
|
|
|
|
|
|
|
#16 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Però in questa maniera non hai molta libertà sul pagesize, che deve essere per forza 65536... forse sarebbe meglio fare tipo così:
Codice:
//pagesize definito altrove
void addHash( lista* list )
{
//ricava hash
unsigned int hash = ...
//ottieni il primo indirizzo "grossolano"
unsigned int pageIndex = hash/pagesize;
Lista** subList = pages[ pageIndex ];
if( subList == NULL )
subList = new Lista**[ pagesize ];
//ora esiste la page necessaria, salvo list
subList [ hash - pageIndex ] = list;
}
D'altra parte, la convenienza di questo approccio dipende dal rapporto vuoti/pagesize... se pagesize fosse sbagliato, si finirebbe per consumare più memoria o con il fare troppe allocazioni. E poi vuoi mettere usare un Lista***
|
|
|
|
|
|
#17 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Ok, era solo per fare un esempio, poi le dimensioni le può cambiare come più vuole
|
|
|
|
|
|
#18 |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
uuuuuuuuuuuuuu strepitoso ^^
Grazie Tommo per l'idea
__________________
iMac 27" 5K |
|
|
|
|
|
#19 |
|
Senior Member
Iscritto dal: Jul 2006
Città: Bergamo
Messaggi: 401
|
Ok i consigli mi sono stati mega utilissime, ma ora ho qualche piccolo problema tecnico
Per prima cosa mi sono inmplementato l'hashset in modo semplice con un array di puntatori a liste: Codice:
template <class key, class hashFunc = defaultHashFunc<key> > class hashset {
typedef unsigned short size_type; //tipo riferito alla dimensione (max 65535)
typedef key value_type; //tipo riferito al dato
typedef hashFunc hasher; //tipo riferito alla funzione di hashing
typedef lista<value_type> collision;// tipo riferito alla lista di collisioni
typedef collision* col_ptr; //tipo riferito al puntatore alla lista di collisioni
col_ptr* data; //array di puntatori alle liste di collisioni
size_type max_size; //dimensione massima dell'hashset
hasher hash; //funzione di hashing
...ecc...
Codice:
~hashset() {
delete[] data;
data = 0;
max_size = 0;
}
Ecco come ho scritto la classe: Codice:
class const_iterator : public iterator<bidirectional_iterator_tag, const key, ptrdiff_t, const key*, const key&> {
friend class hashset;
col_ptr* dato;
col_ptr* first;
col_ptr* last;
const_iterator(col_ptr* d, col_ptr* f, col_ptr* l) : dato(d), first(f), last(l) {}
...ecc ecc metori ++(), --(), ecc ecc...
Codice:
const collision &operator*() const {
return *(*dato);
}
Codice:
hashset<int> hs; hs.insert(20); hs.insert(30); hs.insert(40); cout << *(hs.begin()) << endl; Grazie per la pazienza e l'aiuto Ciauz
__________________
iMac 27" 5K |
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 09:10.




















