Content deleted Content added
replace Range (mathematics) (a redirect to a disambig page) with plain english word -- not being used in a technical sense here |
m HTTP to HTTPS for SourceForge |
||
(46 intermediate revisions by 30 users not shown) | |||
Line 1:
{{Short description|Hash function without any collisions}}
[[File:Hash table 4 1 1 0 0 0 0 LL.svg|thumb|240px|right|A perfect hash function for the four names shown]]
[[File:Hash table 4 1 0 0 0 0 0 LL.svg|thumb|240px|right|A minimal perfect hash function for the four names shown]]
In [[computer science]], a '''perfect hash function''' {{mvar|h}} for a set {{mvar|S}} is a [[hash function]] that maps distinct elements in {{mvar|S}} to a set of {{mvar|m}} integers, with no [[hash collision|collisions]]. In mathematical terms, it is an [[injective function]].
Perfect hash functions may be used to implement a [[lookup table]] with constant worst-case access time. A perfect hash function
Disadvantages of perfect hash functions are that {{mvar|S}} needs to be known for the construction of the perfect hash function. Non-dynamic perfect hash functions need to be re-constructed if {{mvar|S}} changes. For frequently changing {{mvar|S}} [[dynamic perfect hashing|dynamic perfect hash functions]] may be used at the cost of additional space.<ref name="DynamicPerfectHashing" /> The space requirement to store the perfect hash function is in {{math|''O''(''n'')}} where {{math|''n''}} is the number of keys in the structure.
The important performance parameters for perfect hash functions are the evaluation time, which should be constant, the construction time, and the representation size.
==Application==
A perfect hash function with values in a limited range can be used for efficient lookup operations, by placing keys from {{mvar|S}} (or other associated values) in a [[lookup table]] indexed by the output of the function. One can then test whether a key is present in {{mvar|S}}, or look up a value associated with that key, by looking for it at its cell of the table. Each such lookup takes [[constant time]] in the [[Worst-case complexity|worst case]].<ref name="inventor"/> With perfect hashing, the associated data can be read or written with a single access to the table.<ref>{{citation
| last1 = Lu | first1 = Yi | author1-link = Yi Lu (computer scientist)
| last2 = Prabhakar | first2 = Balaji | author2-link = Balaji Prabhakar
| last3 = Bonomi | first3 = Flavio | title = 2006 IEEE International Symposium on Information Theory | chapter = Perfect Hashing for Network Applications | author3-link = Flavio Bonomi
| doi = 10.1109/ISIT.2006.261567
| pages = 2774–2778
| year = 2006| isbn = 1-4244-0505-X | s2cid = 1494710 }}</ref>
==Performance of perfect hash functions==
The important performance parameters for perfect hashing are the representation size, the evaluation time, the construction time, and additionally the range requirement <math>\frac{m}{n}</math> (average number of buckets per key in the hash table).<ref name="CHD"/> The evaluation time can be as fast as {{math|''O''(''1'')}}, which is optimal.<ref name="inventor"/><ref name="CHD"/> The construction time needs to be at least {{math|''O''(''n'')}}, because each element in {{mvar|S}} needs to be considered, and {{mvar|S}} contains {{mvar|n}} elements. This lower bound can be achieved in practice.<ref name="CHD"/>
The lower bound for the representation size depends on {{mvar|m}} and {{mvar|n}}. Let {{math|''m'' {{=}} (1+ε) ''n''}} and {{mvar|h}} a perfect hash function. A good approximation for the lower bound is <math>\log e - \varepsilon \log \frac{1+\varepsilon}{\varepsilon}</math> Bits per element. For minimal perfect hashing, {{math|ε {{=}} 0}}, the lower bound is {{math|log e ≈ 1.44}} bits per element.<ref name="CHD"/>
==Construction==
Line 25 ⟶ 40:
| title = Storing a Sparse Table with {{math|''O''(1)}} Worst Case Access Time
| volume = 31
| year = 1984
}}</ref>
As {{harvtxt|Fredman|Komlós|Szemerédi|1984}} show, there exists a choice of the parameter {{mvar|k}} such that the sum of the lengths of the ranges for the {{mvar|n}} different values of {{math|''g''(''x'')}} is {{math|''O''(''n'')}}. Additionally, for each value of {{math|''g''(''x'')}}, there exists a linear modular function that maps the corresponding subset of {{mvar|S}} into the range associated with that value. Both {{mvar|k}}, and the second-level functions for each value of {{math|''g''(''x'')}}, can be found in [[polynomial time]] by choosing values randomly until finding one that works.<ref name="inventor"/>
Line 31 ⟶ 47:
The hash function itself requires storage space {{math|''O''(''n'')}} to store {{mvar|k}}, {{mvar|p}}, and all of the second-level linear modular functions. Computing the hash value of a given key {{mvar|x}} may be performed in constant time by computing {{math|''g''(''x'')}}, looking up the second-level function associated with {{math|''g''(''x'')}}, and applying this function to {{mvar|x}}.
A modified version of this two-level scheme with a larger number of values at the top level can be used to construct a perfect hash function that maps {{mvar|S}} into a smaller range of length {{math|''n'' + ''o''(''n'')}}.<ref name="inventor"/>
A more recent method for constructing a perfect hash function is described by {{harvtxt|Belazzougui|Botelho|Dietzfelbinger|2009}} as "hash, displace, and compress". Here a first-level hash function {{mvar|g}} is also used to map elements onto a range of {{mvar|r}} integers. An element {{math|''x'' ∈ ''S''}} is stored in the Bucket {{mvar|B<sub>g(x)</sub>}}.<ref name="CHD" />
Then, in descending order of size, each bucket's elements are hashed by a hash function of a sequence of independent fully random hash functions {{math|(Φ<sub>1</sub>, Φ<sub>2</sub>, Φ<sub>3</sub>, ...)}}, starting with {{math|Φ<sub>1</sub>}}. If the hash function does not produce any collisions for the bucket, and the resulting values are not yet occupied by other elements from other buckets, the function is chosen for that bucket. If not, the next hash function in the sequence is tested.<ref name="CHD" />
To evaluate the perfect hash function {{math|''h''(''x'')}} one only has to save the mapping σ of the bucket index {{math|''g''(''x'')}} onto the correct hash function in the sequence, resulting in {{math|h(x) {{=}} Φ<sub>σ(g(x))</sub>}}.<ref name="CHD" />
Finally, to reduce the representation size, the ({{math|σ(i))<sub>0 ≤ i < r</sub>}} are compressed into a form that still allows the evaluation in {{math|''O''(''1'')}}.<ref name="CHD" />
This approach needs linear time in {{mvar|n}} for construction, and constant evaluation time. The representation size is in {{math|''O''(''n'')}}, and depends on the achieved range. For example, with {{math|''m'' {{=}} 1.23''n''}} {{harvtxt|Belazzougui|Botelho|Dietzfelbinger|2009}} achieved a representation size between 3.03 bits/key and 1.40 bits/key for their given example set of 10 million entries, with lower values needing a higher computation time. The space lower bound in this scenario is 0.88 bits/key.<ref name="CHD" />
{{missing information|section|RecSplit & "fingerprinting" [https://epubs.siam.org/doi/pdf/10.1137/1.9781611976007.14 recsplit paper]|date=March 2023}}
===Pseudocode===
'''algorithm''' ''hash, displace, and compress'' '''is'''
(1) Split S into buckets {{math|B<sub>i</sub> :{{=}} g<sup>−1</sup>({i})∩S,0 ≤ i < r}}
(2) Sort buckets B<sub>i</sub> in falling order according to size |B<sub>i</sub>|
(3) Initialize array T[0...m-1] with 0's
(4) '''for all''' i{{thin space}}∈[r], in the order from (2), '''do'''
(5) '''for''' l{{thin space}}←{{thin space}}1,2,...
(6) '''repeat''' forming K<sub>i</sub>{{thin space}}←{{thin space}}{{{math|Φ}}<sub>l</sub>(x)|x{{thin space}}∈{{thin space}}B<sub>i</sub>}
(6) '''until''' |K<sub>i</sub>|=|B<sub>i</sub>| '''and''' K<sub>i</sub>∩{j|T[j]=1}={{thin space}}∅
(7) '''let''' σ(i):= the successful l
(8) '''for all''' j{{thin space}}∈{{thin space}}K<sub>i</sub> '''let''' T[j]:={{thin space}}1
(9) Transform (σ<sub>i</sub>)<sub>0≤i<r</sub> into compressed form, retaining {{math|''O''(''1'')}} access.
==Space lower bounds==
Line 46 ⟶ 86:
| year = 1984}}.</ref>
For minimal perfect hash functions the information theoretic space lower bound is
:<math>\log_2e\approx1.44</math>
bits/key.<ref name="CHD" />
For perfect hash functions, it is first assumed that the range of {{mvar|h}} is bounded by {{mvar|n}} as {{math|''m'' {{=}} (1+ε) ''n''}}. With the formula given by {{harvtxt|Belazzougui|Botelho|Dietzfelbinger|2009}} and for a [[Universe (mathematics)|universe]] <math>U\supseteq S</math> whose size {{math|{{!}}''U''{{!}} {{=}} ''u''}} tends towards infinity, the space lower bounds is
:<math>\log_2e-\varepsilon \log\frac{1+\varepsilon}{\varepsilon}</math>
bits/key, minus {{math|log(''n'')}} bits overall.<ref name="CHD" />
==Extensions==
===Dynamic perfect hashing===
{{main article|Dynamic perfect hashing}}
Using a perfect hash function is best in situations where there is a frequently queried large set, {{mvar|S}}, which is seldom updated. This is because any modification of the set {{mvar|S}} may cause the hash function to no longer be perfect for the modified set. Solutions which update the hash function any time the set is modified are known as [[dynamic perfect hashing]],<ref name="DynamicPerfectHashing">{{citation
| last1 = Dietzfelbinger | first1 = Martin
| last2 = Karlin | first2 = Anna | author2-link = Anna Karlin
Line 67 ⟶ 114:
===Minimal perfect hash function===
A minimal perfect hash function is a perfect hash function that maps {{mvar|n}} keys to {{mvar|n}} consecutive integers – usually the numbers from {{math|0}} to {{math|''n'' − 1}} or from {{math|1}} to {{mvar|n}}. A more formal way of expressing this is: Let {{mvar|j}} and {{mvar|k}} be elements of some finite set {{mvar|S}}. Then {{mvar|
| last1 = Belazzougui | first1 = Djamal
| last2 = Botelho | first2 = Fabiano C.
| last3 = Dietzfelbinger | first3 = Martin
| contribution = Hash, displace, and compress
| contribution-url =
| doi = 10.1007/978-3-642-04128-0_61
| ___location = Berlin
Line 79 ⟶ 126:
| publisher = Springer
| series = [[Lecture Notes in Computer Science]]
| title = Algorithms - ESA 2009
| volume = 5757
| isbn = 978-3-642-04127-3
| year = 2009| citeseerx = 10.1.1.568.130
| url = https://cmph.sourceforge.net/papers/esa09.pdf
}}.</ref> Assuming that <math>S</math> is a set of size <math>n</math> containing integers in the range <math>[1, 2^{o(n)}]</math>, it is known how to efficiently construct an explicit minimal perfect hash function from <math>S</math> to <math>\{1, 2, \ldots, n\}</math> that uses space <math>n \log_2 e + o(n)</math>bits and that supports constant evaluation time.<ref>{{Citation |last1=Hagerup |first1=Torben |title=Efficient Minimal Perfect Hashing in Nearly Minimal Space |date=2001 |url=http://dx.doi.org/10.1007/3-540-44693-1_28 |work=STACS 2001 |pages=317–326 |access-date=2023-11-12 |place=Berlin, Heidelberg |publisher=Springer Berlin Heidelberg |isbn=978-3-540-41695-1 |last2=Tholey |first2=Torsten|doi=10.1007/3-540-44693-1_28 |url-access=subscription }}</ref> In practice, there are minimal perfect hashing schemes that use roughly 1.56 bits/key if given enough time.<ref name="RecSplit">{{citation
| last1 = Esposito | first1 = Emmanuel
| last2 = Mueller Graf | first2 = Thomas
| last3 = Vigna | first3 = Sebastiano
| contribution = RecSplit: Minimal Perfect Hashing via Recursive Splitting
| doi = 10.1137/1.9781611976007.14
| pages =
| series = [[Proceedings]]
| title = 2020 Proceedings of the Symposium on Algorithm Engineering and Experiments (ALENEX)
| year = 2020
| arxiv = 1910.06416
| doi-access = free
}}.</ref><ref>[https://github.com/iwiwi/minimal-perfect-hash minimal-perfect-hash (GitHub)]</ref>
===k-perfect hashing===
A hash function is {{mvar|k}}-perfect if at most {{mvar|k}} elements from {{mvar|S}} are mapped onto the same value in the range. The "hash, displace, and compress" algorithm can be used to construct {{mvar|k}}-perfect hash functions by allowing up to {{mvar|k}} collisions. The changes necessary to accomplish this are minimal, and are underlined in the adapted pseudocode below:
(4) '''for all''' i{{thin space}}∈[r], in the order from (2), '''do'''
(5) '''for''' l{{thin space}}←{{thin space}}1,2,...
(6) '''repeat''' forming K<sub>i</sub>{{thin space}}←{{thin space}}{{{math|Φ}}<sub>l</sub>(x)|x{{thin space}}∈{{thin space}}B<sub>i</sub>}
(6) '''until''' |K<sub>i</sub>|=|B<sub>i</sub>| '''and''' K<sub>i</sub>∩{j|<u>T[j]=k</u>}={{thin space}}∅
(7) '''let''' σ(i):= the successful l
(8) '''for all''' j{{thin space}}∈{{thin space}}K<sub>i</sub> '''set''' <u>T[j]←T[j]+1</u>
===Order preservation===
A minimal perfect hash function {{mvar|F}} is ''order preserving'' if keys are given in some order {{math|''a''<sub>1</sub>, ''a''<sub>2</sub>, ..., ''a''<sub>''n''</sub>}} and for any keys {{math|''a''<sub>''j''</sub>}} and {{math|''a''<sub>''k''</sub>}}, {{math|''j'' < ''k''}} implies {{math|''F''(''a''<sub>''j''</sub>) < F(''a''<sub>''k''</sub>)}}.<ref>{{Citation |first=Bob |last=Jenkins |contribution=order-preserving minimal perfect hashing |title=Dictionary of Algorithms and Data Structures |editor-first=Paul E. |editor-last=Black |publisher=U.S. National Institute of Standards and Technology |date=14 April 2009 |accessdate=2013-03-05 |url=https://xlinux.nist.gov/dads/HTML/orderPreservMinPerfectHash.html}}</ref> In this case, the function value is just the position of each key in the sorted ordering of all of the keys. A simple implementation of order-preserving minimal perfect hash functions with constant access time is to use an (ordinary) perfect hash function
==Related constructions==
While well-dimensioned hash tables have amortized average O(1) time (amortized average constant time) for lookups, insertions, and deletion, most hash table algorithms suffer from possible worst-case times that take much longer.
A worst-case O(1) time (constant time even in the worst case) would be better for many applications (including [[network router]] and [[memory cache]]s).<ref name="davis" >
Timothy A. Davis.
[https://www.cs.wm.edu/~tadavis/cs303/ch05sm.pdf "Chapter 5 Hashing"]: subsection "Hash Tables with Worst-Case O(1) Access"
</ref>{{rp|41}}
Few hash table algorithms support worst-case O(1) lookup time (constant lookup time even in the worst case). The few that do include: perfect hashing; [[dynamic perfect hashing]]; [[cuckoo hashing]]; [[hopscotch hashing]]; and [[extendible hashing]].<ref name="davis" />{{rp|42-69}}
A simple alternative to perfect hashing, which also allows dynamic updates, is [[cuckoo hashing]]. This scheme maps keys to two or more locations within a range (unlike perfect hashing which maps each key to a single ___location) but does so in such a way that the keys can be assigned one-to-one to locations to which they have been mapped. Lookups with this scheme are slower, because multiple locations must be checked, but nevertheless take constant worst-case time.<ref>{{citation
| last1 = Pagh | first1 = Rasmus | author1-link = Rasmus Pagh
| last2 = Rodler | first2 = Flemming Friche
| doi = 10.1016/j.jalgor.2003.12.002
Line 138 ⟶ 179:
| year = 2004}}.</ref>
==
{{reflist|30em}}
==
*Richard J. Cichelli. ''Minimal Perfect Hash Functions Made Simple'', Communications of the ACM, Vol. 23, Number 1, January 1980.
* [[Thomas H. Cormen]], [[Charles E. Leiserson]], [[Ronald L. Rivest]], and [[Clifford Stein]]. ''[[Introduction to Algorithms]]'', Third Edition. MIT Press, 2009. {{ISBN|978-0262033848}}. Section 11.5: Perfect hashing, pp. 267, 277–282.
* Fabiano C. Botelho, [[Rasmus Pagh]] and Nivio Ziviani. [https://arxiv.org/
* Fabiano C. Botelho and [[Nivio Ziviani]]. [http://homepages.dcc.ufmg.br/~nivio/papers/cikm07.pdf "External perfect hashing for very large key sets"]. 16th ACM Conference on Information and Knowledge Management (CIKM07), Lisbon, Portugal, November 2007.
* Djamal Belazzougui, Paolo Boldi, [[Rasmus Pagh]], and Sebastiano Vigna. [https://web.archive.org/web/20140125080021/http://vigna.dsi.unimi.it/ftp/papers/MonotoneMinimalPerfectHashing.pdf "Monotone minimal perfect hashing: Searching a sorted table with O(1) accesses"]. In Proceedings of the 20th Annual ACM-SIAM Symposium On Discrete Mathematics (SODA), New York, 2009. ACM Press.
* Marshall D. Brain and Alan L. Tharp. "Near-perfect Hashing of Large Word Sets". Software—Practice and Experience, vol. 19(10), 967-078, October 1989. John Wiley & Sons.
* Douglas C. Schmidt, [http://www.dre.vanderbilt.edu/~schmidt/PDF/gperf.pdf GPERF: A Perfect Hash Function Generator], C++ Report, SIGS, Vol. 10, No. 10, November/December, 1998.
* Hans-Peter Lehmann, Thomas Mueller, Rasmus Pagh, Giulio Ermanno Pibiri, Peter Sanders, Sebastiano Vigna, Stefan Walzer, "Modern Minimal Perfect Hashing: A Survey", {{arxiv|2506.06536}}, June 2025. Discusses post-1997 developments in the field.
==
*[https://www.gnu.org/software/gperf/ gperf] is an [[
*[http://burtleburtle.net/bob/hash/perfect.html Minimal Perfect Hashing (bob algorithm)] by Bob Jenkins
*[
*[http://sux.di.unimi.it/ Sux4J]: open source monotone minimal perfect hashing in Java
*[https://web.archive.org/web/20130729211948/http://www.dupuis.me/node/9 MPHSharp]: perfect hashing methods in C#
*[https://github.com/rizkg/BBHash BBHash]: minimal perfect hash function in header-only C++
*[https://github.com/rurban/Perfect-Hash Perfect::Hash], perfect hash generator in Perl that makes C code. Has a "prior art" section worth looking at.
|