为什么“计数排序”不是一种更广泛使用的算法?

时间:2015-05-08 03:58:00

标签: python algorithm python-2.7 sorting

我正在制作一些大型学术文件中的字母频率。作为此过程的一部分,是将这些文档的大量剪辑中的字母排序为字母顺序。我正在使用内置排序功能的Python's,我开始怀疑是否可以让它更快。然后我编写了以下函数:

  def count_sort(l):
        items = {'a':0,'b':0,'c':0,'d':0,'e':0,'f':0,'g':0,'h':0,'i':0,'j':0,'k':0,'l':0,'m':
 0,'n':0,'o':0,'p':0,'q':0,'r':0,'s':0,'t':0,'u':0,'v':0,'w':0,'x':0,'y':0,'z'
:0}
        for item in l:
            items[item] += 1
        sort_l = []
        for key in items:
            sort_l += key*items[key]
        return sort_l

sorted字母长的文字字符串上测试此代码与10000时,它的速度几乎快20X

有了这样的性能提升,为什么这个排序算法不在标准libs

5 个答案:

答案 0 :(得分:10)

您重新发现了counting sort算法。

引用维基百科:

  

对于最大键值显着的问题实例   小于项目数,计数排序可以很高   节省空间,作为它使用的唯一存储而不是其输入和   输出数组是使用空间O(k)的Count数组。

计数排序算法变得越来越有效(相对),正在排序的项目总数唯一项目数之间的差异越大被分类。

您可以看到为什么必须查看您自己的代码或Wikipedia example code

# calculate the histogram of key frequencies:
for x in input:
    count[key(x)] += 1

# calculate the starting index for each key:
total = 0
for i in range(k):   # i = 0, 1, ... k-1
    oldCount = count[i]
    count[i] = total
    total += oldCount

# copy to output array, preserving order of inputs with equal keys:
for x in input:
    output[count[key(x)]] = x
    count[key(x)] += 1

return output

你的函数中有2个for循环:第一个迭代你正在排序的字母,第二个迭代 items 字典。正如我之前提到的那样,这意味着项目字典比你正在排序的列表要小得多,但如果相对于被排序的项目数量的独特元素的数量增加,它很快变得非常低效。

就像@BrenBarn回答的那样,只有当你确切知道所期望的字符并且你愿意忽略任何其他字符时。虽然在您给出的示例中计算排序效率非常高,但排序字母的问题并不是最常见的排序问题。

下面我修复了你的函数,通过迭代列表来打印字母,而不是遍历字典中的键(因为Python的字典没有被排序)

def count_sort(l):
    letters = [chr(i) for i in range(97, 122)]
    items = dict()
    for letter in letters:
        items[letter] = 0
    for item in l:
        items[item] += 1
    sort_l = list()
    for letter in letters:
        sort_l.extend(letter*items[letter])
    return sort_l

答案 1 :(得分:3)

正如上面的评论和答案中所提到的,你可以重新发现计数排序,但你还没有发现python collections库:

from collections import Counter
def count_sort(l):
    items = Counter()
    for item in l:
        items[item] += 1
    sort_l = []
    for key in items.keys().sorted():
        sort_l += key*items[key]
    return sort_l

主要区别在于您不会获得任何缺失条目的条目,您可能还希望更改:

        sort_l += key*items[key]

为:

        sort_l.append((key, items[key]))

返回键和计数的排序列表。另一个好方法是返回collections.OrderedDict对象。

答案 2 :(得分:0)

有两个原因。

首先,您的算法依赖于提前知道要排序的列表中将存在哪些元素。如果列表包含大写字符或数字或其他任何内容,则代码将失败并显示错误。

另一个原因是你的代码依赖于字典的顺序保持不变。字典不是有序的,因此无法保证for key in items将按字母顺序迭代键。你的"排序"列表可能根本没有排序。

答案 3 :(得分:0)

如其他答案所述,它被称为Counting Sort ...

除了他们提到的原因/限制外,

计算排序以某种方式使用离散项作为数组索引并计算它们

因此,如果项目的域名是 double / floating point ,那么 实现可能很难,至少可能你必须使用Map()将数字映射到有效的数组索引,而Map()本身是O(lg N),原来复杂性仍然是O(N lg N)。 ..

答案 4 :(得分:0)

如果您对计数排序以及它如何与其他排序算法一起工作感兴趣,您应该检查Counting sort and Radix sort的分析以及每个排序算法的实例:

很多时候,计数排序可以作为更大排序(最值得注意的是Radix sort)算法的子例程,在这种情况下它本身是不切实际的。