大多数情况下,我们使用内置库进行排序,这是通用的。但是大多数时候,我们也基于数字索引或可以在索引中转换的其他值进行排序。如果我没弄错的话,排序数字是O(n)。那么为什么我们根本不使用数字排序算法?
答案 0 :(得分:3)
我不确定(单个)整数(或浮点数,尽管大多数数字排序需要/对整数有效)正在排序的大部分时间'因此,有一些只适用于整数的算法似乎并不特别有用。我说'单身'整数而不是包含多个整数,数字,字符串或其他任何内容的(字符串或)对象(或等效对象)。
更不用说(我相信)任何真实世界计划的瓶颈(其主要目的不仅仅是排序数据)(好吧,大多数)不应该正在排序'单身'使用O(n log n)
排序的数字。您可能更好地改变数据的表示方式,以消除对排序的需求,而不是减少log n
因素。
这是一种常见的误解,但没有排序算法(数字或其他)实际上是最坏情况O(n)
。总会有一些额外的参数发挥作用。对于基数排序,数字的长度是决定因素。对于短数组中的长数,此长度很容易超过log n
,导致性能低于O(n log n)
排序(请参阅下面的测试)。
现在数字排序 有用且比任何基于比较的排序算法更好,因为您的数据符合特定约束 大多数(但不是全部)时间(通过观察任何体面参考给出的复杂性,你应该很容易看出是什么决定它是好的 - 例如O(kN)意味着长数字会导致它花费更长的时间,比如处理重复的问题有点微妙。)
如果没有广泛的实际经验/理论知识,您不太可能选择最有效的算法它完全有可能是您&#39 ;发现自己遇到的问题是,由于某些微妙的因素,所选算法在理论上应该 awesome 严重低于你的数据的标准算法。
因此,标准库不会让您选择不正确的排序并且可能具有糟糕的性能,因为您的数据不符合某些约束条件。图书馆的排序往往是全面的,但并不专门针对特定的数据集。虽然我确信还有一些库专注于排序算法,允许您从广泛的算法中选择,但程序员可能不希望/不应该暴露给普通的Joe这个选择。
另请注意,虽然它们通常不包含在库中,但应该很容易找到/编写您希望使用的任何(流行)类型的实现...然后您应该根据库类别进行基准测试在提交之前对您的数据进行充分的样本处理。
这绝不是一个确凿的,100%正确的测试,具有基数排序和快速排序的最佳实现,可以看到光明的一天。它更多的是表明数据看起来在任何给定算法的性能中起着重要作用。
This是唯一不错的基准,包括我在几分钟的搜索中找到的基数排序。
我运行了代码,发现了这个:(数字范围 0-2147483646 )
(时间单位与纳秒相关,但实际上并没有转化为秒数)
ArraySize Radix Quick
10 1889 126
100 2871 2702
1000 18227 38075
10000 360623 484128
100000 2306284 6029230
快速排序对于大范围的数字和小于100的数组(正如我上面所说的那样)更快。有趣,但没有什么真正令人惊讶的。我的意思是谁关心排序少于100个数字的表现?
但是,看看当我将数字范围更改为 0-99 时发生了什么:
ArraySize Radix Quick
10 1937 121
100 8932 2022
1000 29513 14824
10000 236669 125926
100000 2393641 1225715
对于合理大小的数组(1000-100000个元素),快速排序始终比Radix-sort快2倍左右。
你一定在想 - "世界上有什么?我认为基数排序应该是擅长这些。我的意思是......那里只有2位数。为什么快速排序比上述情况快得多?"究竟。这就是"广泛的现实世界经验/理论知识"进来。我怀疑它与每个算法/实现处理重复的程度有关。但其中一半可能是因为我可能没有针对较小范围优化基数排序实现(我们不知道我们这样做了吗?那么,这是反对尝试在通用基数排序中的另一个原因库)
现在0-99可能不是你的典型数据集,总体而言,基数排序可能仍然更好,但你需要从这一切中拿走:
关于大量排序算法。他们擅长的方面差别很大。不要期望标准库为每个人提供一个功能。基于比较的排序可以对任何可比较的数据类型进行排序(对于大多数实际应用来说足够快),而不是只能对数字进行排序的数字排序。因此,在您的(如在您,编写它的人)库中使用单个(或2,如Java)基于比较的排序是首选。
答案 1 :(得分:1)
基本上,我们使用基于比较的排序算法,因为它更容易。能够提供比较功能并将数据分类是一个巨大的胜利,从工程角度来看,即使你以速度命中付出代价。
请记住,基于O(n log n)比较的排序绑定计算比较,而不是总运行时间。例如,如果你要对字符串进行排序,那么比较可能会花费时间与所比较的字符串的长度呈线性关系。
一个常见的误解(我在其他答案中得到回应)是当你对中等数量的长数字进行排序时,基于比较的排序最终会有更快的渐近复杂度;说他们每个都是k字节。这根本不是真的;你做了n log(n)个数字比较,每个比较需要O(k)时间,总复杂度为O(k n log n)。这比<(k n)更差。
设计快速基数类型比理论说的要困难一些。虽然理论规定您应该选择尽可能大的基数,但在选择的基数和分区输入流时所实现的位置之间存在权衡。较大的基数意味着更少的传递,但也减少了本地对内存的使用。