MIT 6.1220 — Lecture 6

Hash Functions (Part I)

Problem. (Dictionary) A dictionary is a mapping from distinct keys to values; equivalently, it's a collection of objects {xi}\{x_i\} such that each object xix_i has attributes xi.Keyx_i.\textsc{Key} and xi.Valuex_i.\textsc{Value}. Our dictionary must support:

Let U\mathcal{U} denote the universe of all possible keys, and let nn denote the number of objects in the dictionary.

We want Un|\mathcal{U}| \gg n, of course. For simplicity, in this lecture we'll assume UZ0\mathcal{U} \subseteq \mathbb{Z}^{\geq 0}.

Solution. (Solution #1: Huge Array) Just allocate a huge array of size U|\mathcal{U}|. This supports every operation in Θ(1)\Theta(1) time, but requires Θ(U)\Theta(|\mathcal{U}|) space, which is terribly inefficient.

Solution. (Solution #2: Doubly-Linked List) Just store everything in a doubly-linked list. This requires only Θ(n)\Theta(n) space, but takes Θ(n)\Theta(n) time for all three operations.

Solution. (Solution #3: Balanced Binary Tree) Solution #2, except every operation only takes Θ(logn)\Theta(\log n) time!

To do even better, we'll need to use hash functions.

Definition (Hash Function). We seek to reduce the universe size by building a hash function h:U{0,1,,m1}h: \mathcal{U} \to \{0, 1, \dots, m - 1\}. This gives us the time complexity of Solution #1, but the space complexity of Solution #2!

Here, say mm is the size of the hash table, and say α:=nm\alpha := \frac{n}{m} is its load factor. We have the following concern:

Problem. (Injectivity) Evidently Um|\mathcal{U}| \gg m, so hh is not injective. What if two keys have the same image?

Solution. (Hashing & Chaining) Each of {0,1,,m1}\{0, 1, \dots, m - 1\} maintains a doubly-linked list. Every (k,v)(k, v) is appended to the head of the doubly-linked list of h(k)h(k).

Then Insert(k,v)\textsc{Insert}(k, v) takes Θ(1)\Theta(1) time, and our space complexity is Θ(m+n)\Theta(m + n). Meanwhile,

To be explicit: the cost of Delete(k)\textsc{Delete}(k) and Search(k)\textsc{Search}(k) depends on the length of the doubly-linked list of h(k)h(k).

More generally, any deterministic hash function will fail in the worst case. But random hash functions…

Problem. (Randomization Goal) Consider any worst-case set of keys k1,k2,,knUk_1, k_2, \dots, k_n \in \mathcal{U}. Still, then, we have:

Pr[operations k1,k2,,kn all take Θ(1+α)]0.999.\mathrm{Pr}\left [\text{operations } k_1, k_2, \dots, k_n \text{ all take } \Theta\left(1 + \alpha \right) \right ] \geq 0.999.

Note that Pr\mathrm{Pr} is taken over the hash function randomization, not the choice of keys.

To check your understanding… note that we don't seek, more strongly, this instead:

Pr[ k1,k2,,kn, the operations k1,k2,,kn all take Θ(1+α)]0.999.\mathrm{Pr}\left [ \forall \ k_1, k_2, \dots, k_n, \text{ the operations } k_1, k_2, \dots, k_n \text{ all take } \Theta\left(1 + \alpha\right) \right ] \geq 0.999.

It's because the event described above is literally impossible, for reasons explained earlier.

Problem. (Simpler Goal) Instead, we only ask that E[running time of any operation]O(1+α)\mathbb{E}[\text{running time of any operation}] \leq O(1 + \alpha).

Proving this simpler goal does not imply the original goal, but the proof idea is similar.

Claim. Uniform random hashing—i.e. drawing hh uniformly from all functions U{0,1,,m1}\mathcal{U} \to \{0, 1, \dots, m - 1\}—satisfies the simpler goal.

Proof: Just linearity of expectation.   \blacksquare

Except it's not really the end. How, exactly, do we implement hh itself? Note that hh is, itself, a dictionary!

Definition. Call a family of hash functions H{all hash functions h:U{0,1,,m1}}\mathcal{H} \subseteq \{\text{all hash functions } h: \mathcal{U} \to \{0, 1, \dots, m - 1\}\} uniform if:

PrhH[h(k)=]=1m,    kU,    0m1\underset{h \sim \mathcal{H}}{\mathrm{Pr}} [ h(k) = \ell] = \dfrac{1}{m}, ~~~ \forall \ k \in \mathcal{U}, ~~~ \forall \ 0 \leq \ell \leq m - 1

Uniform hash families are not good enough. For example, the uniform family {h:k}=0m1\{h_\ell: k \mapsto \ell \}_{\ell = 0}^{m - 1} is useless.

Definition. Consider some arbitrary sequence of keys k1,,knUk_1, \dots, k_n \in \mathcal{U}. Then for each j=1,2,,nj = 1, 2, \dots, n, we define:

Cj:=#{jjh(kj)=h(kj)}.C_j := \# \{ j' \neq j \mid h(k_{j'}) = h(k_j)\}.

Note that Search(kj)\textsc{Search}(k_j) and Delete(kj)\textsc{Delete}(k_j) both take O(1+Cj)O(1 + C_j) time.

Claim. Uniform random hashing satisfies E[Cj]α\mathbb{E}[C_j] \leq \alpha for all j=1,2,,nj = 1, 2, \dots, n.

Proof: Just linearity of expectation.   \blacksquare

Definition. Call a family of hash functions H{all hash functions h:U{0,1,,m1}}\mathcal{H} \subseteq \{\text{all hash functions } h: \mathcal{U} \to \{0, 1, \dots, m - 1\}\} universal if:

 k1k2,   PrhH[h(k1)=h(k2)]1m.\forall \ k_1 \neq k_2, ~~~ \underset{h \sim \mathcal{H}}{\mathrm{Pr}}[h(k_1) = h(k_2)] \leq \dfrac{1}{m}.

Claim. Any universal hash family satisfies E[Cj]α\mathbb{E}[C_j] \leq \alpha for all j=1,2,,nj = 1, 2, \dots, n.

By corollary, any universal hash family achieves our goal of E[running time of any operation]O(1+α)\mathbb{E}[\text{running time of any operation}] \leq O(1 + \alpha).

Proof: Just LoE again. We've generalized uniform random hashing in a way that preserves the proof.   \blacksquare

Problem. (Universal Hash Family) How can we define a universal hash family?

Solution. (Universal Hash Family: Failures) We might try the following things:

Now for the actual construction.

Solution. (Universal Hash Family: Success) Here it is.

Proof: This hash family feels universal, and the proof is similarly obvious.

Say k1k2k_1 \neq k_2 first differ at position \ell^* in their base-mm representations.

Then the value of aa_{\ell^*} exactly determines whether ha(k1)=ha(k2)h_a(k_1) = h_a(k_2) or not.   \blacksquare

Remark. We keep writing O(1+α)O(1 + \alpha) instead of just O(1)O(1) or O(α)O(\alpha) because the relationship between mm and nn may vary. (It could be that nmn \ll m or nmn \gg m.)