risposta
Se per sufficiente, è dire se la vostra funzione è iniettiva, la risposta è No. Il ragionamento è che l'insieme di tutte hash rispetta la tua funzione di uscita può ha cardinalità 2^64, mentre il lo spazio dei tuoi ingressi è molto più grande. Tuttavia, questo non è veramente importante, perché non è possibile avere una funzione hash iniettiva data la natura dei tuoi input. Una buona funzione hash ha queste qualità:
- Non è facilmente invertibile. Dato l'output k, non è calcolabile fattibile nel corso della vita dell'universo per trovare m tale che h (m) = k.
- L'intervallo è distribuito uniformemente nello spazio di uscita.
- E 'difficile trovare due ingressi M e M 'tale che h (m) = h (m')
Naturalmente, le estensioni di questi davvero variano a seconda che si desidera qualcosa che è crittograficamente sicuro, o vuoi prendere qualche pezzo di dati arbitrario e semplicemente inviarlo un numero arbitrario a 64 bit. Se vuoi qualcosa di crittograficamente sicuro, scriverlo da solo non è una buona idea. In tal caso, avrai anche bisogno della garanzia che la funzione sia sensibile alle piccole modifiche nell'input. L'oggetto funzione std::hash
non è richiesto per essere crittograficamente sicuro. Esiste per casi d'uso isomorfi alle tabelle hash. CPP Rerefence dice:
Per due parametri diversi k1
e k2
che non sono uguali, la probabilità che std::hash<Key>()(k1) == std::hash<Key>()(k2)
dovrebbe essere molto piccola, si avvicina 1.0/std::numeric_limits<size_t>::max()
.
Mostrerò qui sotto come la vostra attuale soluzione non garantisce questo.
collisioni
ti darò alcune delle mie osservazioni su una variante della soluzione (non so che cosa il vostro membro _class
è).
std::size_t hash_code(const std::unordered_map<std::string, std::string>& m) {
std::hash<std::string> h;
std::size_t result = 0;
for (auto&& p : m) {
result ^= h(p.first)^h(p.second);
}
return result;
}
È facile generare collisioni.Considerate le seguenti mappe:
std::unordered_map<std::string, std::string> container0;
std::unordered_map<std::string, std::string> container1;
container0["123"] = "456";
container1["456"] = "123";
std::cout << hash_code(container0) << '\n';
std::cout << hash_code(container1) << '\n';
Sulla mia macchina, la compilazione con g ++ 4.9.1, questo uscite:
1225586629984767119
1225586629984767119
La questione se questo importa o no si pone. Ciò che è rilevante è la frequenza con cui si avranno mappe in cui le chiavi e i valori sono invertiti. Queste collisioni si verificano tra due mappe in cui gli insiemi di chiavi e valori sono gli stessi.
ordine di iterazione
Due unordered_map
istanze aventi esattamente le stesse coppie di valori-chiave non avranno necessariamente lo stesso ordine di iterazione. CPP Rerefence dice:
Per due parametri k1
e k2
che sono uguali, std::hash<Key>()(k1) == std::hash<Key>()(k2)
.
Questo è un requisito banale per una funzione hash. La tua soluzione evita questo perché l'ordine di iterazione non ha importanza dal momento che XOR è commutativo.
una possibile soluzione
Se non avete bisogno di qualcosa che è crittograficamente sicuro, è possibile modificare la soluzione un po 'di uccidere la simmetria. Questo approccio va bene nella pratica per tabelle hash e simili. Questa soluzione è anche indipendente dal fatto che l'ordine in un unordered_map
non è definito. Utilizza la stessa proprietà utilizzata dalla soluzione (Commutatività di XOR).
std::size_t hash_code(const std::unordered_map<std::string, std::string>& m) {
const std::size_t prime = 19937;
std::hash<std::string> h;
std::size_t result = 0;
for (auto&& p : m) {
result ^= prime*h(p.first) + h(p.second);
}
return result;
}
Tutto ciò che serve in una funzione di hash in questo caso è un modo per mappare una coppia chiave-valore a un buon valore hash arbitrario, e un modo per combinare gli hash delle coppie chiave-valore utilizzando un commutativa operazione. In questo modo, l'ordine non ha importanza. Nell'esempio hash_code
ho scritto, il valore hash della coppia valore-chiave è solo una combinazione lineare dell'hash della chiave e dell'hash del valore. Puoi costruire qualcosa di un po 'più complicato, ma non ce n'è bisogno.
's/concatenate/XOR' e dovresti essere pronto. Quindi solo le cose che una funzione di hash deve essere in grado di fare è generare lo stesso hash per due valori semanticamente equivalenti e distribuire il suo output in modo ragionevolmente uniforme sull'insieme di tutti i possibili valori di hash. –
@dyp OP vuole eseguire l'hash del contenitore stesso. –
Fondamentalmente la tua domanda è come ottenere un hash per un intervallo di valori (non ordinato) e in realtà non è specifico per 'std :: unordered_map'? – inf