大多数排序算法依赖于成对比较,确定A< B,A = B或A>乙
我正在寻找利用成对比较功能的算法(以及奖励积分,Python中的代码),这种功能可以将少一点或多一点与更多一点相区别。所以也许而不是返回{-1,0,1},比较函数返回{-2,-1,0,1,2}或{-5,-4,-3,-2,-1,0,1 ,2,3,4,5}或甚至是区间(-1,1)上的实数。
对于某些应用程序(例如近序排序或近似排序),这样可以通过较少的比较来确定合理的排序。
答案 0 :(得分:7)
您可以使用修改后的快速排序。让我解释比较函数返回[-2,-1,0,1,2]时的示例。比如,你有一个数组A来排序。
创建5个空数组 - Aminus2,Aminus1,A0,Aplus1,Aplus2。
选择A,X的任意元素。
对于数组的每个元素,将其与X进行比较。
根据结果,将元素放在Aminus2,Aminus1,A0,Aplus1,Aplus2阵列之一中。
递归地对Aminus2,Aminus1,Aplus1,Aplus2应用相同的排序(注意:你不需要对A0进行排序,因为它的所有元素都等于X)。
连接数组以获得最终结果:A = Aminus2 + Aminus1 + A0 + Aplus1 + Aplus2。
答案 1 :(得分:6)
确实可以使用额外信息来最小化比较总数。对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路比较函数对帮助排序几乎没有作用。例如,如果对于大于4的差异,比较函数返回+/- 2,对于差异为4或更小的比率,+ / - 1,则比较次数仅减少5%。根本原因是在开始时使用的课程粒度分区只有少数“近似匹配”,其他所有内容都属于“远程匹配”。
超级比较的改进是覆盖对数范围(如果在十分之内,则为+/- 1,如果在一百以内,则为+/- 2,如果在一千以内,则为+/-。
理想的比较功能是自适应的。对于任何给定的序列大小,比较函数应努力将序列细分为大致相等大小的分区。信息理论告诉我们,这将最大化每次比较的信息位数。
自适应方法也具有良好的直观感。首先应该将人们划分为 love vs 之类的,然后再进行更精致的区分,比如爱情和爱情。进一步的分区通道应该各自进行更精细和更精细的区分。
答案 2 :(得分:1)
似乎使用raindog的修改后的快速排序可以让你更快地流出结果,也许可以更快地进入页面。
也许这些功能已经可以通过精心控制的qsort操作获得?我没想过多少。
这听起来有点像基数排序,除了不是查看每个数字(或其他类型的桶规则),你从丰富的比较中编造桶。我很难想到有丰富的比较可用的情况,但数字(或类似的东西)不是。
答案 3 :(得分:1)
我想不出任何真正有用的情况。即使我可以,我怀疑对模糊值进行排序所需的额外CPU周期将超过您提到的那些“额外比较”。但我仍然会提出一个建议。
考虑这种可能性(所有字符串都使用27个字符a-z和_):
11111111112
12345678901234567890
1/ now_is_the_time
2/ now_is_never
3/ now_we_have_to_go
4/ aaa
5/ ___
显然字符串1和2更相似,1和3以及很多比1和4更相似。
一种方法是缩放每个相同字符位置的差值,并使用第一个不同字符设置最后一个位置。
暂时搁置标志,将字符串1与2进行比较,将位置8与'n' - 't'区别开来。这是6的差异。为了将其转换为单个数字1-9,我们使用公式:
digit = ceiling(9 * abs(diff) / 27)
因为最大差值为26.最小差值1变为数字1.最大差值26变为数字9.我们的差值6变为3.
由于差异在第8位,因此比较函数将返回3x10 -8 (实际上它将返回负数,因为字符串1来自在字符串2之后
对字符串1和4使用类似的过程,比较函数返回-5x10 -1 。最高可能的回报(字符串4和5)在' - ' - 'a'(26)的位置1有差异,它产生数字9,因此给出9x10 -1 。
获取这些建议并根据需要使用它们。我很想知道你的模糊比较代码最终会如何运作。
答案 4 :(得分:1)
考虑到您希望根据人体比较订购多个项目,您可能希望像体育比赛那样处理这个问题。您可以允许每个人类投票将获胜者的分数增加3并将宽松减少3,+ 2和-2,+ 1和-1或仅0 0进行抽奖。
然后你只需根据分数进行常规排序。
另一种选择是单人或双人淘汰锦标赛结构。
答案 5 :(得分:0)
您可以使用两个比较来实现此目的。将更重要的比较乘以2,并将它们加在一起。
这是我在Perl中的意思的一个例子。 它通过第一个元素比较两个数组引用,然后通过第二个元素比较。
use strict;
use warnings;
use 5.010;
my @array = (
[a => 2],
[b => 1],
[a => 1],
[c => 0]
);
say "$_->[0] => $_->[1]" for sort {
($a->[0] cmp $b->[0]) * 2 +
($a->[1] <=> $b->[1]);
} @array;
a => 1 a => 2 b => 1 c => 0
您可以非常轻松地将其扩展到任意数量的比较。
答案 6 :(得分:0)
也许有充分的理由这样做,但我认为它不会影响任何特定情况的替代方案,肯定对一般情况不利。原因?除非您对输入数据的域名和值的分布有所了解,否则您无法真正改进,例如,快速排序。如果你做知道这些事情,通常会有更有效的方法。
反示例:假设您的比较对于数字差异超过1000的数字返回“巨大差异”值,并且输入为{0,10000,20000,30000,...}
反示例:与上述相同,但输入{0,10000,10001,10002,20000,20001,...}
但是,你说,我知道我的输入看起来不像那样!那么,在这种情况下,请详细告诉我们您的输入真实情况。然后有人可能真的帮助。
例如,一旦我需要对历史数据进行排序。数据保持分类。添加新数据后会附加,然后再次运行列表。我没有附加新数据的信息。我为这种情况设计了一种混合排序,通过选择快速排序已排序的数据并在遇到未排序的数据时将其调整为快速(实际上切换到qsort),轻松击败qsort和其他人。
您要改进通用排序的唯一方法是了解您的数据。如果你想要答案,你将不得不在这里进行沟通。