Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4460964
  • 博文数量: 1214
  • 博客积分: 13195
  • 博客等级: 上将
  • 技术积分: 9105
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-19 14:41
个人简介

C++,python,热爱算法和机器学习

文章分类

全部博文(1214)

文章存档

2021年(13)

2020年(49)

2019年(14)

2018年(27)

2017年(69)

2016年(100)

2015年(106)

2014年(240)

2013年(5)

2012年(193)

2011年(155)

2010年(93)

2009年(62)

2008年(51)

2007年(37)

分类: Python/Ruby

2014-09-23 14:56:54

文章来源:
2010-05-07 22:50 高铁军译 人民邮电出版社 字号: | 
一键收藏,随时查看,分享好友!

《Python Cookbook(第2版)中文版》第5章,引言由Tim Peters撰写,本章覆盖了Python中的搜索和排序技术。很多例子展示了将稳定快速的list.sort和decorate-sort-undecorate(DSU)(在Python 2.4中新导入的能力)结合在一起的创造性的应用,其余例子还展示了heapq、bisect的威力,并介绍了Python中其他的搜索和排序工具。本节为大家介绍获取序列中最小的几个元素。

AD:


5.8  获取序列中最小的几个元素

感谢:Matteo Dell'Amico、Raymond Hettinger、George Yoshida、Daniel Harding

任务

你需要从一个给定的序列中获取一些最小的元素。可以将序列排序,然后使用seq[:n],但还有没有更好的办法呢?

解决方案

如果需要的元素数目n远小于序列的长度,也许还能做得更好。sort是很快的,但它的时间复杂度仍然是O(nlogn),但如果n很小,我们获取前n个最小元素的时间是O(n)。下面给出一个简单可行的生成器,在Python 2.3和2.4中都同样有效:

		
  1. import heapq  
  2. def isorted(data):  
  3.       data = list(data)  
  4.       heapq.heapify(data)  
  5.       while data:  
  6.             yield heapq.heappop(data) 

在Python 2.4中,如果事先知道n,还有更简单和更快的方法从data中获取前n个最小的元素:

		
  1. import heapq  
  2. def smallest(n, data):  
  3.      return heapq.nsmallest(n, data) 

讨论

data可能是任何有边界的可迭代对象,解决方案中的isorted函数通过调用list来确保它是序列。也可以删掉data = list(data)这一行,假如下列条件满足的话:你知道data是一个序列,你并不关心生成器是否重新排列了data的元素,而且需要在获取的同时从data中删除元素。

如同5.7节所示,Python标准库提供了heapq模块,它支持人们熟知的数据结构- 堆。解决方案中的isorted先创建了一个堆(通过heap.heapify),然后在每一步获取元素的时候(通过heap.heappop),生成并删除堆中最小的元素。

在Python 2.4中,heapq模块引入了两个新函数。heapq.nlargest (n, data) 返回的是一个长度为n的data中最大的元素的列表,heapq.nsmallest (n, data) 则返回一个包含前n个最小元素的列表。这些函数并不要求data满足堆的条件,它们甚至不要求data是一个列表-任何有边界的、元素是可以比较的可迭代对象都适用。解决方案中的函数smallest除了调用heapq.smallest,其实什么也没干。

关于速度,我们总是应该进行实际测量,猜测不同代码片段的相对运行速度是毫无意义的行为。因此,当我们只是循环获取前几个(最小)元素的时候,isorted的性能和Python 2.4内建的sorted函数的性能比起来究竟怎么样呢?为了进行测试,我写了一个top10函数,它可以调用这两种方法,然后我也为Python 2.3实现了一个sorted函数,因为在Python 2.3中此函数并未得到原生的支持:

		
  1. try:  
  2.      sorted  
  3. except:  
  4.      def sorted(data):  
  5.             data = list(data)  
  6.             data.sort( )  
  7.             return data  
  8. import itertools  
  9. def top10(data, howtosort):  
  10.      return list(itertools.islice(howtosort(data), 10)) 

在我的计算机上,在Python 2.4中处理一个洗过牌的1 000个整数的列表,top10调用isorted耗时260 s,但采用内建的sorted则耗时850 s。而Python 2.3甚至还要慢得多:isorted耗时12ms,sorted耗时2.7ms。换句话说,Python 2.3的sorted比Python 2.4的sorted要慢3倍,但在isorted上则要慢50倍。需要记住这个重要的经验:当需要进行优化时,首先进行测量。不应该仅仅根据基本原理选择优化的方式,因为真实的性能数据变化多端,即使在两个兼容的发行版本之间也会有很大的差异。第二个经验是:如果真的很关心性能,那赶紧转移到Python 2.4吧。和Python 2.3相比,Python 2.4经过了极大的优化和加速,特别是在搜索和排序的方面。

如果确定你的代码只需要支持Python 2.4,那么,如同本节解决方案所建议的那样,应当使用heapq的新函数nsmallest。它速度快、使用简便,远胜于自己编写代码。举个例子,实现Python 2.4中的top10,你只需要:

		
  1. import heapq  
  2. def top10(data):  
  3.       return heapq.nsmallest(10, data) 

比起前面展示的那个基于isorted的top10,对同样的洗过牌的1 000个整数的列表进行处理,消耗的时间还可以削减一半。

更多资料

Library Reference和Python in a Nutshell中list类型的sort方法,以及heapq和timeit模块;第19章中关于Python的迭代的内容;Python in a Nutshell中有关优化的章节;Python源码中的heap.py所包含的关于堆的有趣的讨论;5.7节关于heapq的介绍。

阅读(1058) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~