|
|
Line 1: |
Line 1: |
| '''Extendible hashing''' is a type of [[hash function|hash]] system which treats a hash as a bit string, and uses a [[trie]] for bucket lookup.<ref>{{Citation | title=Extendible Hashing - A Fast Access Method for Dynamic Files | journal=ACM Transactions on Database Systems | volume=4 | issue=3 | date=September 1979 | first1=R. | last1=Fagin | first2=J. | last2=Nievergelt | first3=N. | last3=Pippenger | first4=H. R. | last4=Strong | pages=315–344 | doi=10.1145/320083.320092 }}</ref> Because of the hierarchical nature of the system, re-hashing is an incremental operation (done one bucket at a time, as needed). This means that time-sensitive applications are less affected by table growth than by standard full-table rehashes.
| | Friends contact him Royal. What she enjoys performing is to perform croquet but she hasn't produced a dime with it. Bookkeeping is how he supports his family members and his salary has been really satisfying. Alabama has always been his house.<br><br>My site - extended auto warranty, [http://www.empredators.de/index.php?mod=users&action=view&id=10734 click through the following website page], |
| | |
| ==Example==
| |
| | |
| This is an example from {{harvtxt|Fagin|Nievergelt|Pippenger|Strong|1979}}.
| |
| | |
| Assume that the hash function <math>h(k)</math> returns a binary number. The first i bits of each string will be used as indices to figure out where they will go in the "directory" (hash table). Additionally, i is the smallest number such that the first i bits of all keys are different.
| |
| | |
| Keys to be used:
| |
| | |
| <math>h(k_1)</math> = 100100<br>
| |
| <math>h(k_2)</math> = 010110<br>
| |
| <math>h(k_3)</math> = 110110
| |
| | |
| Let's assume that for this particular example, the bucket size is 1. The first two keys to be inserted, k<sub>1</sub> and k<sub>2</sub>, can be distinguished by the most significant bit, and would be inserted into the table as follows:
| |
| | |
| [[Image:Extendible hashing 1.svg|200px]]
| |
| | |
| Now, if k<sub>3</sub> were to be hashed to the table, it wouldn't be enough to distinguish all three keys by one bit (because k<sub>3</sub> and k<sub>1</sub> have 1 as their leftmost bit. Also, because the bucket size is one, the table would overflow. Because comparing the first two most significant bits would give each key a unique location, the directory size is doubled as follows:
| |
| | |
| [[Image:Extendible hashing 2.svg|200px]]
| |
| | |
| And so now k<sub>1</sub> and k<sub>3</sub> have a unique location, being distinguished by the first two leftmost bits. Because k<sub>2</sub> is in the top half of the table, both 00 and 01 point to it because there is no other key to compare to that begins with a 0.
| |
| | |
| === Further detail ===
| |
| | |
| <math>h(k_4)</math> = 011110
| |
| | |
| Now, k<sub>4</sub> needs to be inserted, and it has the first two bits as 01..(1110), and using a 2 bit depth in the directory, this maps from 01 to Bucket A. Bucket A is full (max size 1), so it must be split; because there is more than one pointer to Bucket A, there is no need to increase the directory size.
| |
| | |
| What is needed is information about:
| |
| | |
| # The key size that maps the directory (the global depth), and
| |
| # The key size that has previously mapped the bucket (the local depth)
| |
| | |
| In order to distinguish the two action cases:
| |
| | |
| # Doubling the directory when a bucket becomes full
| |
| # Creating a new bucket, and re-distributing the entries between the old and the new bucket
| |
| | |
| Examining the initial case of an extendible hash structure, if each directory entry points to one bucket, then the local depth should be equal to the global depth.
| |
| | |
| The number of directory entries is equal to 2<sup>global depth</sup>, and the initial number of buckets
| |
| is equal to 2<sup>local depth</sup>.
| |
| | |
| Thus if global depth = local depth = 0, then 2<sup>0</sup> = 1, so an initial directory of one pointer to one bucket.
| |
| | |
| Back to the two action cases:
| |
| | |
| If the local depth is equal to the global depth, then there is only one pointer to the bucket, and there is no other directory pointers that can map to the bucket, so the directory must be doubled (case1).
| |
| | |
| If the bucket is full, if the local depth is less than the global depth,
| |
| then there exists more than one pointer from the directory to the bucket, and the bucket can be split (case 2).
| |
| | |
| [[Image:Extendible hashing 3.svg|200px]]
| |
| | |
| Key 01 points to Bucket A, and Bucket A's local depth of 1 is less than the directory's global depth of 2, which means keys hashed to Bucket A have only used a 1 bit prefix (i.e. 0), and the bucket needs to have its contents split using keys 1 + 1 = 2 bits in length; in general, for any local depth d where d is less than D, the global depth, then d must be incremented after a bucket split, and the new d used as the number of bits of each entry's key to redistribute the entries of the former bucket into the new buckets.
| |
| | |
| [[Image:Extendible hashing 4.svg|200px]]
| |
| | |
| Now,
| |
| <math>h(k_4)</math> = 011110<br>
| |
| is tried again, with 2 bits 01.., and now key 01 points to a new bucket but there is still k2 in it (<math>h(k2)</math> = 010110 and also begins with 01).
| |
| | |
| If k2 had been 000110, with key 00, there would have been no problem, because k2 would have remained in the new bucket A' and bucket D would have been empty.
| |
| | |
| (This would have been the most likely case by far when buckets are of greater size than 1 and the newly split buckets would be exceedingly unlikely to overflow, unless all the entries were all rehashed to one bucket again. But just to emphasize the role of the depth information, the example will be pursued logically to the end.)
| |
| | |
| So Bucket D needs to be split, but a check of its local depth, which is 2, is the same as the global depth, which is 2, so the directory must be split again, in order to hold keys of sufficient detail, e.g. 3 bits.
| |
| | |
| [[Image:Extendible hashing 5.svg|200px]]
| |
| | |
| # Bucket D needs to split due to being full.
| |
| # As D's local depth = the global depth, the directory must double to increase bit detail of keys.
| |
| # Global depth has incremented after directory split to 3.
| |
| # The new entry k4 is rekeyed with global depth 3 bits and ends up in D which has local depth 2, which can now be incremented to 3 and D can be split to D' and E.
| |
| # The contents of the split bucket D, k2, has been re-keyed with 3 bits, and it ends up in D.
| |
| # K4 is retried and it ends up in E which has a spare slot.
| |
| | |
| [[Image:Extendible hashing 6.svg|200px]] | |
| | |
| Now, <math>h(k2)</math> = 010110 is in D and <math>h(k_4)</math> = 011110 is tried again, with 3 bits 011.., and it points to bucket D which already contains k2 so is full; D's local depth is 2 but now the global depth is 3 after the directory doubling, so now D can be split into bucket's D' and E, the contents of D, k2 has its <math>h(k2)</math> retried with a new global depth bitmask of 3 and k2 ends up in D', then the new entry k4 is retried with <math>h(k_4)</math> bitmasked using the new global depth bit count of 3 and this gives 011 which now points to a new bucket E which is empty. So K4 goes in Bucket E.
| |
| | |
| ==Example implementation == | |
| | |
| Below is the extendible hashing algorithm in [[Python (programming language)|Python]], with the disc block / memory page association, caching and consistency issues removed. Note a problem exists if the depth exceeds the bit size of an integer, because then doubling of the directory or splitting of a bucket won't allow entries to be rehashed to different buckets.
| |
| | |
| The code uses the ''least significant bits'', which makes it more efficient to expand the table, as the entire directory can be copied as one block ({{harvtxt|Ramakrishnan|Gehrke|2003}}).
| |
| | |
| === Python example ===
| |
| <source lang="python">
| |
| PAGE_SZ = 20
| |
| | |
| class Page(object):
| |
| | |
| def __init__(self):
| |
| self.m = {}
| |
| self.d = 0
| |
| | |
| def full(self):
| |
| return len(self.m) >= PAGE_SZ
| |
| | |
| def put(self,k,v):
| |
| self.m[k] = v
| |
| | |
| def get(self,k):
| |
| return self.m.get(k)
| |
| | |
| class EH(object):
| |
| | |
| def __init__(self):
| |
| self.gd = 0
| |
| p = Page()
| |
| self.pp= [p]
| |
| | |
| def get_page(self,k):
| |
| h = hash(k)
| |
| p = self.pp[ h & (( 1 << self.gd) -1)]
| |
| return p
| |
| | |
| def put(self, k, v):
| |
| p = self.get_page(k)
| |
| if p.full() and p.d == self.gd:
| |
| self.pp *= 2
| |
| self.gd += 1
| |
|
| |
| if p.full() and p.d < self.gd:
| |
| p.put(k,v);
| |
| p1 = Page()
| |
| p2 = Page()
| |
| for k2,v2 in p.m.items():
| |
| h = hash(k2)
| |
| h = h & ((1 << self.gd) -1)
| |
| if (h >> p.d) & 1 == 1:
| |
| p2.put(k2,v2)
| |
| else:
| |
| p1.put(k2,v2)
| |
| for i,x in enumerate(self.pp):
| |
| if x == p:
| |
| if (i >> p.d) & 1 == 1:
| |
| self.pp[i] = p2
| |
| else:
| |
| self.pp[i] = p1
| |
| | |
| p2.d = p1.d = p.d + 1
| |
| else:
| |
| p.put(k, v)
| |
| | |
| def get(self, k):
| |
| p = self.get_page(k)
| |
| return p.get(k)
| |
| | |
| if __name__ == "__main__":
| |
| eh = EH()
| |
| N = 10088
| |
| l = list(range(N))
| |
| | |
| import random
| |
| random.shuffle(l)
| |
| for x in l:
| |
| eh.put(x,x)
| |
| print l
| |
| | |
| for i in range(N):
| |
| print eh.get(i)
| |
| | |
| </source>
| |
| | |
| ==Notes==
| |
| {{Reflist}}
| |
| | |
| == See also ==
| |
| * [[Trie]]
| |
| * [[Hash table]]
| |
| * [[Stable hashing]]
| |
| * [[Consistent hashing]]
| |
| * [[Linear hashing]]
| |
| | |
| ==References==
| |
| * {{Citation | title=Extendible Hashing - A Fast Access Method for Dynamic Files | journal=ACM Transactions on Database Systems | volume=4 | issue=3 | date=September 1979 | first1=R. | last1=Fagin | first2=J. | last2=Nievergelt | first3=N. | last3=Pippenger | first4=H. R. | last4=Strong | pages=315–344 | doi=10.1145/320083.320092}}
| |
| * {{Citation | title=Database Management Systems, 3rd Edition: Chapter 11, Hash-Based Indexing | year=2003 | first1=R. | last1=Ramakrishnan | first2=J. | last2=Gehrke | pages=373–378 }}
| |
| | |
| == External links ==
| |
| * {{DADS|Extendible hashing|extendibleHashing}}
| |
| * [http://www.isqa.unomaha.edu/haworth/isqa3300/fs009.htm Extendible Hashing] at University of Nebraska
| |
| * [http://www.csm.astate.edu/~rossa/datastruc/Extend.html Extendible Hashing notes] at Arkansas State University
| |
| * [http://www.smckearney.com/adb/notes/lecture.extendible.hashing.pdf Extendible hashing notes]
| |
| | |
| [[Category:Search algorithms]]
| |
| [[Category:Hashing]]
| |