我正在调查和性能测试随机化有序集合的各种方法,我正在考虑传递一个只是随机返回比较结果的比较委托的选项。例如:
int RandomComparison<T> (T x, T y)
{
return this.random.Next (-1, 2);
}
但是,由于我不知道引擎盖下使用的排序算法,我不知道这是否可能导致排序逻辑永远不会完成。虽然这种方法在性能方面看起来通常不可靠,但我想知道它是否真的危险地无法使用?
答案 0 :(得分:4)
答案 1 :(得分:4)
我猜你的怀疑是正确的。构建这种类型代码的整个前提是如果A&lt; B和B&lt; C然后A&lt;下进行。
您可以将随机数与集合中的每个项相关联,然后对该值进行标准排序。
答案 2 :(得分:3)
List.Sort
实际上是记录使用QuickSort(没有给出额外的细节),但我会忽略这一点,而是赞成谈论一般的排序......
我怀疑对于任何合理的排序算法,这个比较器导致操作以概率1终止,因为当N趋于无穷大时,持续N步的概率趋于0。事实上,我认为在很多情况下都存在严格的上限。
原因是有可能(以非零概率发生)随机比较器可能恰好返回一致的结果,以便在一行中进行足够的比较(可能是O(n log n),或者O( n ^ 2)用于插入排序)算法“认为”它已完成。只要最终发生这种情况,我就会期望它终止排序。
然而,我无法确定这一点,因为在这个短暂的一致性阶段之前,算法进入不可恢复的状态绝不是不可能的。我只是预感到实际的排序算法,这种情况不会发生。事实上,对于许多算法来说,无论比较器有多荒谬,问题都会无情地变小,从而产生一个硬限制。特别是QuickSort通过重复分区数组来工作 - 如果比较器是随机的,那么这将导致无意义的分区,但只要不存在由于不一致而发生的实际错误,数组仍然会被分成两部分用于递归。事实上,“顶部”部分中的元素在第二次询问时不会比枢轴更大,这根本不重要,因为它们永远不会再与它进行比较。
无论如何,它可能需要很长时间,足以构成对大多数实际目的而言“危险”。例如,冒泡排序只会持续搅拌,直到连续获得n
个负面结果,无需移动任何东西即可完成扫描。这需要花费预期的时间2^n
或其左右(特别是List.Sort
不是泡泡排序,我用它来说明一般情况下排序可能会发生什么)。根据实施细节,您可能会发现,由于“不可能”发生,您访问越界,比“完成”更多次。
操作肯定不必然随机化所有排列的集合。再次注意,QuickSort选择一个轴,然后围绕它进行分区。给定这个随机比较器(并且,再次假设没有像越界访问那样的实际错误),第一个选择的枢轴在阵列的最右侧结束的概率是1 ^ 2 ^(n-1) )(因为所有其他元素必须在其左边排序,每个元素有一半的机会),而在均匀随机选择的排列中,该概率应该是n中的1。当它“应该”均匀时,第一个选择的枢轴在阵列上分布在钟形曲线上。
答案 3 :(得分:2)
是的,使用随机比较可以使排序算法以几种不同的方式起作用。它可能会产生偏差,甚至会卡住。
用于混洗集合的最有效算法是Fisher-Yates shuffle。它会在O(n)中对集合进行洗牌,这比任何排序算法都要好。
答案 4 :(得分:1)
正如许多人已经提到的,使用随机比较函数进行排序不会产生排列的均匀分布,因此不应使用。但无论如何要回答这个问题......
从理论的角度来看,是的你可以根据你的排序算法选择无限地挂起。
例如,此冒泡排序一直持续到没有进行交换:
procedure bubbleSort( A : list of sortable items )
do
swapped = false
for each i in 1 to length(A) - 1 inclusive do:
if A[i-1] > A[i] then
swap( A[i-1], A[i] )
swapped = true
end if
end for
while swapped
end procedure
因此,恶意比较函数可以使两个值无限地交换进去。
但是,我能想到的所有不错的算法(quicksort,mergesort,heapsort,insertionsort)只使用for
循环,所以它们的终止不应该依赖于排序例程的正确性。甚至冒泡排序本身也可以通过几次更改来保证终止(请查看wiki链接以获取示例)
所以,从实际的角度来看,不是:比较函数的正确性不应该影响排序的运行时行为*
*在最坏的情况下,像quicksort这样的模数是O(n ^ 2)...
答案 5 :(得分:0)
一旦你传递了一个不提供一致顺序的函数的排序例程,就不能保证会发生什么。人们希望C#没有什么坏处,但我从经验中知道C ++在这种情况下有一种令人不安的倾向于转移核心。