有位朋友用
CString类写了一个Email列表去重程序,结果发现处理78000 行的数据居然用了7分多种。于是他用Python的map(hash实现的)重写了这个程序,最后只用了300ms。CString一定会慢,因为实现中 没有使用hash。于是有人用c写了一个hash函数来实现这个功能,最后花了400ms。但是代码长度是Python代码的数十倍。详情请看这里。
qyb利用glib的hashtable功能也实现了这个功能,速度超过了Python,但是代码也很简洁。qyb说,“这个例子再一次教育我们,熟练掌握优秀的第三方库是多么的重要.”
我这里没有现成的gcc和glib,不好测试qyb的程序。但是仅就那个用来测试Python程序来说,存在很多降低效率的误区,所以,我把这些问题都用效率更加的方式改写了,从而把程序的效率提高了三倍左右,现在与大家分享。
原程序:运行时间0:00:00.609000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
line = f.readline();
while len(line)>0:
if not hashtable.has_key(line):
hashtable[line] = 1
f2.write(line)
line = f.readline();
f.close()
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
显而易见,用f.readline()是不明智的,这样输入速度大减。我们用readlines()来加速。
改进一:运行时间0:00:00.375000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
for line in lines:
if not hashtable.has_key(line):
hashtable[line] = 1
f2.write(line)
f.close()
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
然 后我们看输出,很显然f2.write(line)也是很不明智的,一行一行的输出无论如何都是低效的。writelines可以解决这个问题,我们知道 map的keys()函数可以返回map的键列表,所以, f2.writelines(hashtable.keys())看来是个好选择。
改进二:运行时间0:00:00.281000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
for line in lines:
if not hashtable.has_key(line):
hashtable[line] = 1
f.close()
f2.writelines(hashtable.keys())
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
实际上我们知道采用hashtable[line] = 1方式的时候,如果键已经存在,仅仅会更新键的值而已。所以判断语句
if not hashtable.has_key(line)实际上是无用的,应该去掉。
改进三:运行时间0:00:00.250000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
for line in lines:
hashtable[line] = 1
f.close()
f2.writelines(hashtable.keys())
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
到这里我们发现另外一个问题,我们实际上只需要map的键,而不需要它的值。所以我们直接用操作map的键列表的方式应该可以加速程序的运行。幸好,运行的结果证明了我的推论。
改进四:运行时间0:00:00.188000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
for line in lines:
hashtable.keys().append(line)
f.close()
f2.writelines(hashtable.keys())
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
然后,我遭遇了一个小挫败,我记得有人告诉过我用map函数处理列表应该比for in方式更加高效。但是运行结果没有遵循这个说法,也许是我哪里写错了。不过至少map和lamdba可以让程序看起来简洁一些,或者说更酷,更加让人难懂,哈哈。
改进五:运行时间0:00:00.250000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
map(lambda x:hashtable.keys().append(x),lines)
f.close()
f2.writelines(hashtable.keys())
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
这个方法不是我写的,而是qyb文章后一个匿名网友的留言。应该说这个方法我没有想到的主要原因是我确实不知道set类型,-____-#。这个方法无疑是最简洁的一种,虽然效率不如我的第四种改进方式,也许这是因为一些实现上的问题造成的。
改进六:运行时间0:00:00.219000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
f = file("email.txt","r")
f2 = file("email_new.txt","w")
f2.write("".join(set(f.readlines())))
f.close()
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
我的结论很简单,Python真的很好用,呵呵。
发表于 @ 2006年07月16日 23:40:00|评论(loading...)|编辑