额外的信息确实可以用来最小化比较的总数。对 super_comparison 函数的调用可用于进行相当于对常规比较函数的大量调用的扣除。例如,a much-less-than b 和 c little-less-than b 意味着 a < c < b。
可以将扣除项组织成可以单独排序的箱或分区。实际上,这等效于具有 n 路分区的 QuickSort。这是 Python 中的一个实现:
from collections import defaultdict
from random import choice
def quicksort(seq, compare):
'Stable in-place sort using a 3-or-more-way comparison function'
# Make an n-way partition on a random pivot value
segments = defaultdict(list)
pivot = choice(seq)
for x in seq:
ranking = 0 if x is pivot else compare(x, pivot)
segments[ranking].append(x)
seq.clear()
# Recursively sort each segment and store it in the sequence
for ranking, segment in sorted(segments.items()):
if ranking and len(segment) > 1:
quicksort(segment, compare)
seq += segment
if __name__ == '__main__':
from random import randrange
from math import log10
def super_compare(a, b):
'Compare with extra logarithmic near/far information'
c = -1 if a < b else 1 if a > b else 0
return c * (int(log10(max(abs(a - b), 1.0))) + 1)
n = 10000
data = [randrange(4*n) for i in range(n)]
goal = sorted(data)
quicksort(data, super_compare)
print(data == goal)
通过使用 trace 模块检测此代码,可以测量性能增益。在上面的代码中,常规的三路比较使用了 133,000 次比较,而超级比较函数将调用次数减少到了 85,000 次。
该代码还可以轻松地尝试各种比较功能。这将表明,朴素的 n 路比较函数对排序几乎没有帮助。例如,如果比较函数对于大于四的差异返回 +/-2,对于差异小于等于四的差异返回 +/-1,则比较次数仅会适度减少 5%。根本原因是一开始使用的课程粒度分区只有少数“近匹配”,而其他所有内容都属于“远匹配”。
对超级比较的改进是覆盖对数范围(即,如果在十以内,则为 +/-1,如果在一百以内,则为 +/-2,如果在一千以内,则为 +/-。
理想的比较函数应该是自适应的。对于任何给定的序列大小,比较函数应努力将序列细分为大小大致相等的分区。信息论告诉我们,这将使每次比较的信息位数最大化。
自适应方法也具有很好的直观意义。人们应该首先被划分为love vs like,然后再进行更精细的区分,例如love-a-lot vs love-a-little。进一步的划分通道应该会做出越来越精细的区分。