我正在制作一些大型学术文件中的字母频率。作为此过程的一部分,是将这些文档的大量剪辑中的字母排序为字母顺序。我正在使用内置排序功能的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
?
答案 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)算法的子例程,在这种情况下它本身是不切实际的。