插值排名方法确实没有问题。只需根据表示 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位字符串类型似乎足以满足我所描述的键。我需要验证的是整理顺序是否正确。
此外,同样的推理适用于任何k>=2
. 第一项得到 key k/2
。还有一个简单的优化可以防止在末尾和前面分别附加和前置元素的非常常见的情况导致长度为 O(n) 的键。对于这些情况,它保持 O(log n) (尽管在内部插入同一位置仍然可以在 p 次插入后产生 O(p) 键)。我会让你解决这个问题。使用 k=256,您可以使用不定长度的字节字符串。在 SQL 中,我相信你会想要varbinary(max)
. SQL 提供正确的字典排序顺序。如果你有一个插值操作的实现很容易BigInteger
包类似于 Java 的。如果您喜欢人类可读的数据,您可以将字节字符串转换为例如十六进制字符串 (0-9a-f) 并存储它们。那么正常的 UTF8 字符串排序顺序是正确的。