使用Python在2M内存中排序一百万个32位整数
译言网 | 使用Python在2M内存中排序一百万个32位整数
使用Python在2M内存中排序一百万个32位整数
译者:小鼠发表时间:2008-11-13浏览量:6757评论数:2挑错数:0作者演示了如何在2M内存的环境下,完成对一百万个32位整数排序.有人开玩笑地问我 如何使用python在2M内存中排序一百万个32位整数.为了应付这个挑战,我学习了一下缓冲I/O.
很 明显,这是一个开玩笑的问题.假设是二进制编码,单单是数据就已经占了4M!唯一的解释就是: 给定一个包含一百万个32位整数的文件,你如何使用最少内存去排序好它们?这可能需要使用某种合并排序的方式,把数据分块在内存排序,并保存到临时文件中 去,最后把临时文件合并获得最终结果.
下面是我的解决方案,稍候会解释.
注意:所有的例子都是使用python3.0. 这个例子的不同之处就是使用file.buffer访问二进制流的方式去访问字符流文件.#!/usr/bin/env python3.0
import sys, array, tempfile, heapq
assert array.array('i').itemsize == 4 def intsfromfile(f):
while True:
a = array.array('i')
a.fromstring(f.read(4000))
if not a:
break
for x in a:
yield x iters = []
while True:
a = array.array('i')
a.fromstring(sys.stdin.buffer.read(40000))
if not a:
break
f = tempfile.TemporaryFile()
array.array('i', sorted(a)).tofile(f)
f.seek(0)
iters.append(intsfromfile(f)) a = array.array('i')
for x in heapq.merge(*iters):
a.append(x)
if len(a) >= 1000:
a.tofile(sys.stdout.buffer)
del a[:]
if a:
a.tofile(sys.stdout.buffer)在 我的Goole桌面(一个使用了3年,跑着Googlified Linux的个人电脑,用Python3.0的pystones测试得分是34000),在输入文件包含一百万个32位随机整数的情况下,这个程序大概花 了5.4秒.还不太差,如果直接在内存中排序大概需要2秒:
#!/usr/bin/env python3.0
import sys, array
a = array.array('i', sys.stdin.buffer.read())
a = list(a)
a.sort()
a = array.array('i', a)
a.tofile(sys.stdout.buffer)回到合并排序方案.头三行非常明显:
#!/usr/bin/env python3.0
import sys, array, tempfile, heapq
assert array.array('i').itemsize == 4第一行表示我们使用Python3.0.第二行引入将使用的模块.第三行检查到64位系统时中断程序,因为64位系统中'i'的类型值不是表示32位整数;这里我没有考虑代码的移植性.
然后我们定义一个辅助函数,它是一个从文件读入的整数的生成器:def intsfromfile(f):
while True:
a = array.array('i')
a.fromstring(f.read(4000))
if not a:
break
for x in a:
yield x这 个地方已经调优了算法性能: 它每次读入1000个整数,然后一个个yield.我原来没有用到缓冲 -- 它就每次只从文件读入4个字节,转换成整数,然后yield.但程序跑起来就慢了4倍!要注意的是我们不能使用a.fromfile(f,1000)因为 fromfile()方法在文件没有足够的值的情况下会出错,并且我想要代码能自动适应文件中的整数.(我原先设想大概会写入10,000个整数到一个临 时文件中.)
接着我们进入循环.反复地从输入文件读入10,000个整数,在内存中排序,然后写入临时文件.我们为临时文件增加一个迭代器,通过上面的intsfromfile()函数,使其成为迭代器中的一个列表,以备在随后的合并阶段使用.iters = []
while True:
a = array.array('i')
a.fromstring(sys.stdin.buffer.read(40000))
if not a:
break
f = tempfile.TemporaryFile()
array.array('i', sorted(a)).tofile(f)
f.seek(0)
iters.append(intsfromfile(f))要注意的是,为了应对包含一百万个值的输入,程序将会创建100个包含10,000个值的临时文件.
最后我们合并这些文件(每个都已经排序). heapq模块有一个非常实用的函数: heapq.merge(iter1, iter2, ...)它把多个已排序的输入值(如本例中的)合并排序,返回一个有序的迭代器。a = array.array('i')
for x in heapq.merge(*iters):
a.append(x)
if len(a) >= 1000:
a.tofile(sys.stdout.buffer)
del a[:]
if a:
a.tofile(sys.stdout.buffer)这里也是一个必不可少使用缓冲I/O的地方:当一个值可用的时候就写入文件,这样会成倍地减慢算法速度.因此,通过简单地增加输入输出缓冲,可以获得上十倍的速度提升!
这就是程序的主要思路了.
另外我们还能体验到heapq模块的强大,它包含了在输出阶段需要的合并迭代器功能。当然,array模块管理二进制数据的便利也是令人印象深刻的。
最后,通过这个例子让你们知道,Python 3.0相对于Python2.5来说差别并不是很大!
使用Python在2M内存中排序一百万个32位整数的更多相关文章
- 详解Python变量在内存中的存储
这篇文章主要是对python中的数据进行认识,对于很多初学者来讲,其实数据的认识是最重要的,也是最容易出错的.本文结合数据与内存形态讲解python中的数据,内容包括: 引用与对象 可变数据类型与不可 ...
- C#中判断系统的架构(32位,还是64位)
一种很简单的方法就是根据IntPtr类型的Size属性来判断, //IntPtr.Size在64位为8,在32位为4 public static Boolean Is64Bit() { ) retur ...
- python 将IP地址转换成打包后的32位格式
python 2.7 #!/usr/bin/env python # Python Network Programming Cookbook -- Chapter - # This program r ...
- EF如何操作内存中的数据以及加载相关联表的数据:延迟加载、贪婪加载、显示加载
之前的EF Code First系列讲了那么多如何配置实体和数据库表的关系,显然配置只是辅助,使用EF操作数据库才是每天开发中都需要用的,这个系列讲讲如何使用EF操作数据库.老版本的EF主要是通过Ob ...
- SQL Server 内存中OLTP内部机制概述(一)
----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory ...
- EF如何操作内存中的数据和加载外键数据:延迟加载、贪婪加载、显示加载
EF如何操作内存中的数据和加载外键数据:延迟加载.贪婪加载.显示加载 之前的EF Code First系列讲了那么多如何配置实体和数据库表的关系,显然配置只是辅助,使用EF操作数据库才是每天开发中都需 ...
- python的变量内存管理
一.变量的引用机制 当你在python中定义一个值,如x = 500时,python会在内存中开辟一个小地方用于存储数值. x = 500 #定义一个变量 print(id(x)) #打印该变量的内存 ...
- 第十七章——配置SQLServer(2)——32位和64位系统中的内存配置
原文:第十七章--配置SQLServer(2)--32位和64位系统中的内存配置 前言: 本文讲述32位和64位系统中的内存配置,在SQLServer 2005/2008中,DBA们往往尝试开启AWE ...
- 转:如何在32位程序中突破地址空间4G的限制
//如何在32位程序中突破地址空间4G的限制 //首先要获得内存中锁定页的权限 #define _WIN32_WINNT 0x0501 //xp系统 #include <windows.h> ...
随机推荐
- 重新签名apk文件(手工用命令行)
re-sign.jar中后自动去除签名这个方法,经试验不可用! 1.去除准备重新签名SinaVoice.apk软件本身的签名 将apk文件后缀改为.zip,然后从winrar中删除META-INF文件 ...
- ssh登录的时候,根本不给输入密码的机会,直接拒绝,是因为BatchMode的设置
BatchMode no“BatchMode”如果设为“yes”,passphrase/password(交互式输入口令)的提示将被禁止.当不能交互式输入口令的时候,这个选项对脚本文件和批处理任务十分 ...
- UIViewController加载过程
UIViewController是视图和数据的桥梁,UIViewController是所有controller的基类,ios内置了很多试图控制器,如导航控制器,tableViewController等 ...
- Git本地分支版本号过低导致的push错误 error: failed to push some refs to ... 及兴许amend
今天在用git的时候遇到了一个问题.在想远程分支push的时候,出现了以下的错误: ! [remote rejected] master -> refs/for/master (change 1 ...
- 事务管理在三层架构中应用以及使用ThreadLocal再次重构
本篇将详细讲解如何正确地在实际开发中编写事务处理操作,以及在事务处理的过程中使用ThreadLocal的方法. 在前面两篇博客中已经详细地介绍和学习了DbUtils这个Apache的工具类,那么在本篇 ...
- (08)DBA写给开发的索引经验
索引可是个大事情,翻开任意一本数据库调优的书,索引都会占到比较大的篇幅.这是个人人都很重视的问题,可往往起始阶段还好,但数据库到最后常常还是会陷入由索引起的性能怪圈中.特别是在上线运行过一 ...
- Unknown database 'DB_NAME'
Cannot create PoolableConnectionFactory (Unknown database 'DB_NAME'): com.mysql.jdbc.exceptions.jdbc ...
- 在WEB工程的web层中的编程技巧
本篇以看传智播客方立勋老师的<JDBC入门>之<实现客户关系管理案例>视频有感,从中提取方老师在设计管理系统的简单案例中对自己比较有用的部分,以便日后在开发过程中希望能有所帮助 ...
- 发掘ListBox的潜力(一):自动调整横向滚动条宽度
<自绘ListBox的两种效果>一文帖出之后,从反馈信息来看,大家对这种小技巧还是很认同.接下来我将继续围绕ListBox写一系列的文章,进一步发掘ListBox的潜力,其中包括:自动调整 ...
- boost asio 异步实现tcp通讯
---恢复内容开始--- asioboost 目录(?)[-] 一前言 二实现思路 通讯包数据结构 连接对象 连接管理器 服务器端的实现 对象串行化 一.前言 boost asio可算是一个简 ...