插值秩方法确实没有问题。只需定义您自己的编号系统,该编码系统基于可变长度的位向量,表示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