插值秩方法確實沒有問題。只需定義您自己的編號系統,該編碼系統基於可變長度的位向量,表示0到1之間的二進制分數,不含尾隨零。二進制點位於第一位數字的左側。
該系統的唯一不便之處在於空位向量給出的最小可能密鑰爲0。因此,只有在您肯定的情況下,您纔會使用它,相關的項目將永遠是第一個列表元素。通常情況下,只給第一項爲鍵1.這相當於1/2,因此在範圍(0..1)中的隨機插入將傾向於最小化比特的使用。之前和之後插一個項目,
01 < newly interpolated = 1/4
1
11 < newly interpolated = 3/4
要再次插:
001 < newly interpolated = 1/8
01
011 < newly interpolated = 3/8
1
101 < newly interpolated = 5/8
11
111 < newly interpolated = 7/8
請注意,如果你願意,你可以省略存儲最後1!所有密鑰(除非你通常使用的0除外)都以1結尾,因此存儲它是非常有用的。
比較二進制分數很像詞法比較:0 < 1,並且從左到右掃描的第一個位差告訴你哪個更小。如果沒有差異發生,即一個矢量是另一個矢量的嚴格前綴,則較短的那個更小。
有了這些規則,想出一個算法來接受兩個位向量並計算一個大致(或在某些情況下)在它們之間的結果是非常簡單的。只需添加位串,然後右移1,丟棄不必要的尾位,即取兩者的平均值來分割它們之間的範圍。
在上面的例子中,如果缺失已經給我們留下了:
01
111
,我們需要插這些,加上01(0)
和和111
獲得1.001
,然後轉移到獲得1001
。這可以很好地作爲插值。但是請注意,最後的1
不必要地使其長於任一操作數。一個簡單的優化是放棄最後的1
位以及尾隨零來獲得簡單的1
。果然,1
大概是我們希望的一半。
當然,如果您在同一位置執行多次插入操作(例如,想像列表開始處的連續插入操作),位向量將變長。這與在二叉樹中的相同點處插入完全相同。它長得很長,很纖細。爲了解決這個問題,你必須在同步期間通過用最短可能的位向量重新編號來「重新平衡」,例如,對於14你會使用上面的序列。
加成
雖然我還沒有嘗試過,Postgres的bit string type似乎足以爲我所描述的鑰匙。我需要驗證的是整理順序是正確的。
此外,對於任何k>=2
,同樣的推理可以很好地處理base-k數字。第一項獲得鑰匙k/2
。還有一個簡單的優化,可以防止常見的在末端和前端添加和預先添加元素的情況,導致長度爲O(n)的鍵。它爲這些情況維護O(log n)(儘管在內部插入相同的地方仍然可以在p插入後生成O(p)鍵)。我會讓你解決這個問題。 k = 256時,可以使用無限長度的字節字符串。在SQL中,我相信你會想要varbinary(max)
。 SQL提供正確的詞典排序順序。如果你有一個類似於Java的BigInteger
包,插值操作的實現很容易。如果您喜歡可讀的數據,則可以將字節字符串轉換爲十六進制字符串(0-9a-f)並存儲它們。然後正常的UTF8字符串排序順序是正確的。
如果你在兩個系統都有'{a,b,c}',並且系統A插入'p'來獲得'{a,b,p,c}',系統B插入'p' {a,p,b,c}',當你同步時你想要以什麼順序結束? – Geoff 2012-04-12 20:08:13
@Geoff,有兩個p的機率幾乎爲零,因爲我們使用的是隨機UUID。 – 2012-04-12 20:10:52
對不起,你是對的。我真正想問的是如何按排序順序處理碰撞。在我改變之前,我寫道:\t 如果你在這兩個系統上都有'{a,b,c}',並且系統A插入'p'來獲得'{a,b,p,c}'和系統B插入'q'得到'{a,b,q,c}',當你同步時,你想要結束的'p'和'q'的順序是什麼? – Geoff 2012-04-12 20:16:26