Algorithm Search⌘K

Proofs: Persistent trie

These are the human proofs of Persistent trie, at full length.

The persistent trie keeps every version and answers counts and maximum XOR correctly full

This proof shows: correctness.

Fix w≥1w \ge 1. For a node tt and a depth hh with 0≤h≤w0 \le h \le w, call tt well formed at depth hh if either h=wh = w and t.cnt≥0t.cnt \ge 0, or h<wh < w and both children are well formed at depth h+1h + 1 with t.cnt=t.child[0].cnt+t.child[1].cntt.cnt = t.child[0].cnt + t.child[1].cnt. The node ∅\varnothing is well formed at every depth, since its count is 0 and it is its own child. For tt well formed at depth hh and a string ss of w−hw - h bits, write t[s]t[s] for the node reached by following ss, and define the multiset ms(t,h)\text{ms}(t, h) in which each ss has multiplicity t[s].cntt[s].cnt. A version root is well formed at depth 0, and its multiset is read with ss the ww bits of a key, from bit w−1w - 1 down.

Lemma 1. If tt is well formed at depth hh, then t.cnt=∣ms(t,h)∣t.cnt = |\text{ms}(t, h)|, the total multiplicity, and for a prefix qq of length jj, t[q].cntt[q].cnt is the number of elements of ms(t,h)\text{ms}(t, h) that start with qq.

Proof. By induction on w−hw - h, from the additivity of counts at each inner node. □\square

Lemma 2 (add). Let tt be well formed at depth 0, xx a key, and dd an integer with t[x].cnt+d≥0t[x].cnt + d \ge 0. Then ADD(t,x,d)(t, x, d) returns a root t′t' that is well formed at depth 0 with ms(t′)=ms(t)\text{ms}(t') = \text{ms}(t) changed by dd copies of xx. It makes exactly w+1w + 1 nodes and writes only to nodes it made.

Proof. Let t0=t,t1,…,twt_0 = t, t_1, \ldots, t_w be the old nodes on the path of xx, and c0,…,cwc_0, \ldots, c_w the new nodes made in lines L1 and L5, so chc_h starts with the children and the count of tht_h, plus dd. Line L6 sets ch.child[bh]=ch+1c_{h}.child[b_h] = c_{h+1}, where bhb_h is the bit used at depth hh, and this is the only write. After the loop, chc_h has the child ch+1c_{h+1} on the path side and the old child th.child[1−bh]t_h.child[1 - b_h] on the other side. By induction from h=wh = w up: cwc_w is a leaf with count tw.cnt+d≥0t_w.cnt + d \ge 0. For h<wh < w, ch.cnt=th.cnt+d=th.child[bh].cnt+th.child[1−bh].cnt+d=ch+1.cnt+th.child[1−bh].cntc_h.cnt = t_h.cnt + d = t_h.child[b_h].cnt + t_h.child[1-b_h].cnt + d = c_{h+1}.cnt + t_h.child[1 - b_h].cnt, so chc_h is well formed. A string ss that leaves the path at depth hh reaches the same node through chc_h as through tht_h, so its multiplicity is unchanged; the string xx reaches cwc_w, whose count is the old one plus dd. The nodes chc_h are made by this call, so no other version reaches them while they are written. □\square

Lemma 3 (no change). After an operation returns, no node reachable from an existing root is written again.

Proof. By Lemma 2, ADD writes only to nodes that it made and that no root reached before it returned. COUNT and MAX-XOR do not write. □\square

Theorem 1. After any sequence of operations, every version root is well formed at depth 0, and its multiset is the one that the abstract type gives to the version. COUNT returns the multiplicity of xx and REMOVE never makes a negative count.

Proof. By induction on the operations. Version 0 is ∅\varnothing, which is well formed with the empty multiset. insert is ADD with d=1d = 1, right by Lemma 2. REMOVE calls ADD with d=−1d = -1 only if COUNT is positive (line L9), so the precondition of Lemma 2 holds, and otherwise returns the old root, whose multiset has no xx to remove. COUNT follows the path of xx to its leaf and returns its count, the multiplicity by definition. By Lemma 3 no earlier version changes. □\square

Theorem 2 (maximum XOR). Let uu and vv be version roots with ms(v)⊇ms(u)\text{ms}(v) \supseteq \text{ms}(u) as multisets, let D=ms(v)−ms(u)D = \text{ms}(v) - \text{ms}(u) be non-empty, and let yy be a key. Then MAX-XOR(u,v,y)(u, v, y) returns max⁡x∈Dx⊕y\max_{x \in D} x \oplus y.

Proof. By Lemma 1, for each prefix qq, v[q].cnt−u[q].cntv[q].cnt - u[q].cnt is the number of elements of DD that start with qq. We claim that before the step for bit ii, the nodes uu and vv are u[q]u[q] and v[q]v[q] for a prefix qq of length w−1−iw - 1 - i such that some element of DD starts with qq, and rr is the value of the top w−1−iw - 1 - i bits of q⊕yq \oplus y (as a number), which equal the top bits of the optimum M=max⁡x∈Dx⊕yM = \max_{x \in D} x \oplus y. At the start qq is empty, DD is non-empty by line L12, and r=0r = 0. In a step, let bb be bit ii of yy. If some element of DD starts with q(1−b)q(1 - b) (line L15), then such an xx has bit ii of x⊕yx \oplus y equal to 1, while all elements that start with qq agree with MM on the higher bits; since 2i>∑j<i2j2^i > \sum_{j < i} 2^j, every optimum starts with q(1−b)q(1 - b), so bit ii of MM is 1, and the step keeps the claim. Otherwise every element of DD that starts with qq continues with bb, so bit ii of MM is 0, and line L16 keeps the claim. After the step for bit 0, qq has length ww and rr is MM. ■\blacksquare

Back to Persistent trie