Python 更快地比较两个列表
我试图对两个文件进行比较,这些文件大约有70k行,根据我目前的算法,我大约需要5分钟来完全比较所有文件 基本上,我所做的是把两个文件的所有行都放到列表中,这样看起来就像这样Python 更快地比较两个列表,python,algorithm,Python,Algorithm,我试图对两个文件进行比较,这些文件大约有70k行,根据我目前的算法,我大约需要5分钟来完全比较所有文件 基本上,我所做的是把两个文件的所有行都放到列表中,这样看起来就像这样 compare_list_new=[['Albert','V4','25.000','45.000','1.3500'], ['James','V4','22.000','43.000','1.4000'], ['James','V5','23.000','41.000','1.3000']]
compare_list_new=[['Albert','V4','25.000','45.000','1.3500'],
['James','V4','22.000','43.000','1.4000'], ['James','V5','23.000','41.000','1.3000']]
compare_list_old=[['Albert','V4','25.000','45.000','1.3900'],
['James','V4','22.000','43.000','1.2000'], ['James','V5','23.000','41.000','1.2000']]
这两个文件都有相似的名称,所以要在旧条目中找到新条目,我们必须根据坐标进行搜索,所以如果我想从新到旧找到一个特定的James,我必须使用“22.000”、“43.000”
找到条目后,我从新文件中取1.4000,从旧文件中取1.2000,然后减去它们,从旧到新找到增量
这是Im使用的当前算法:
# This is not important
import time
import timeit
import bisect
from operator import itemgetter
import time
compare=open("factor.output.new.txt","w")
compare_list_new=[]
compare_list_old=[]
newlist=[]
#File Count algorithm
start = time.time() # Tracks execution time
def list_create(fname): #Makes the list in the appropriate format
newlist=[]
with open(fname) as file:
for i, line in enumerate(file):
if i>6:
for line in file:
lines_list=line.split(" ")
del lines_list[0]
del lines_list[2:29]
del lines_list[5:12]
newlist.append(lines_list)
return newlist
#Creates lists and sorts them
compare_list_new=list_create("par_iop.pwr.sfactor.output_new.ipf")
compare_list_new=sorted(compare_list_new, key=itemgetter(2))
compare_list_old=list_create("par_iop.pwr.sfactor.output_old.ipf")
compare_list_old=sorted(compare_list_old, key=itemgetter(2))
compare.write("Name Version Coordinate_x Coordinate_y Sfactordelta FLAG\n")
compare_list_copy=compare_list_old #Makes a copy of the list
for item in compare_list_new: # compares both lists
end = time.time()
print(end - start)
for line in compare_list_old:
if item[0:4] == line[0:4]:
s1=float(item[4])
s2 = float(line[4])
delta=s1-s2
delta=format(delta,'.4f')
item[4]=str(delta)
text = " ".join(item)
compare.write(text +" " +"\n")
compare_list_copy.remove(line)
match=1
break
if(match==1):
compare_list_old=compare_list_copy
match=0
else:
text=" ".join(item)
compare.write(text + " " + "ITEM NOT FOUND IN OLD FILE BUT IS IN NEW FILE""\n")
try:
compare_list_copy.remove(line)
except ValueError:
pass
compare_list_old = compare_list_copy
compare.close()
本质上,比较两个列表的部分在对它们进行排序后所做的工作如果它们匹配,那么它将执行操作以获取增量并将其从副本中删除,然后使旧的与副本相等,以便在遍历列表时不会删除项。如果项目不匹配,则表示它不在旧文件中,但在新文件中
我想要一些可能使这个过程更快的东西。您当前的比较至少是二次的(因为嵌套循环)。从第一个列表(线性时间)生成字典的速度更快,其中键是名称的元组和前2个坐标(对于新文件和旧文件,它们似乎相同),然后对于第二个列表中的每个项目,检查该键是否在字典中(再次线性时间) 您当前的比较至少是二次的(因为嵌套循环)。从第一个列表(线性时间)生成字典的速度更快,其中键是名称的元组和前2个坐标(对于新文件和旧文件,它们似乎相同),然后对于第二个列表中的每个项目,检查该键是否在字典中(再次线性时间) 这里有很多代码,缩进显然是不正确的,所以我甚至不知道逻辑到底应该是什么,也没有任何迹象表明您认为哪个部分慢(或者您如何知道),但有一件事立即跳出来:
compare_list_copy.remove(line)
…稍后再删除另一个
首先,无论何时调用lst.remove(val)
,列表都必须进行线性搜索,将每个元素与val
进行比较。但是您已经知道所需元素的索引(或者,您可以通过使用枚举来知道它),因此整个搜索都是浪费的;只需dellst[idx]
即可
其次,无论您是删除
还是删除
,您仍然在从数组中间删除。这意味着将所有后续元素向上移动一个插槽。它有一个快得多的常数(它只是一个大的memmove,而不是一堆对比较函数的调用),但它仍然是线性的
这是在你的内部循环中进行的。因此,您将一个额外的因数N
乘以您已经是二次的时间。如果您只是在相同数据上进行对数搜索,而不是线性搜索,那么您通过对分来进行对数搜索的任何努力都将被浪费
如果您需要一些可以在对数时间内搜索,也可以在对数时间内修改的内容,那么您需要的是某种树(或树列表结构,如skiplist)。PyPI上有很好的库包装了各种二叉树和b树变体,或者你可以在维基百科上查找算法
或者,您可以获取类似于排序容器库的内容,该库在更高级别上封装了内容。例如,sorteddict
的作用非常类似于dict
,但您可以搜索最近的键,而不是精确匹配的键,或给定范围内的所有键,等等。在封底下,它可以与某种混合的btree绳或其他东西一起工作,但您不需要关心这些细节;重要的是,它保证在对数时间内完成所有需要的操作
一旦你做到了这一点,你的两个外部循环中至少有一个可以转化为对数搜索(通过使用一棵树,你几乎可以免费得到)
此时您的总时间是O(log**2n*N)
,而不是O(N**3)
,这是一个巨大的差异
如果你不习惯于处理算法复杂性方面的性能,请考虑这一点:只有1000个元素,立方时间取<代码> 1000×1000×1000 < /代码>=10亿个步骤;对数平方线性时间需要
10*10*1000
=100000步。这就是天和秒之间的区别。这里有很多代码,缩进明显不正确,所以我甚至不知道逻辑到底应该是什么,也没有迹象表明你认为哪个部分慢(或者你如何知道),但有一件事立即跳出来:
compare_list_copy.remove(line)
compare_list_new = [['Albert', 'V4', '25.000', '45.000', '1.3500'],
['James', 'V4', '22.000', '43.000', '1.4000'],
['James', 'V5', '23.000', '41.000', '1.3000']]
compare_list_old = [['Albert', 'V4', '25.000', '45.000', '1.3900'],
['James', 'V4', '22.000', '43.000', '1.2000'],
['James', 'V5', '23.000', '41.000', '1.2000']]
d = {}
for l in compare_list_old:
# construct tuple as key and value as 'float' value
d[tuple(l[0:3])] = l[4]
print(d)
# {('Albert', 'V4', '25.000'): '1.3900', ('James', 'V4', '22.000'): '1.2000', ('James', 'V5', '23.000'): '1.2000'}
print(d[('Albert', 'V4', '25.000')])
# 1.3900
for item in compare_list_new:
old_float_val = d[tuple(item[0:3])]
new_float_val = item[4]
# continue whatever calculation here
…稍后再删除另一个
首先,无论何时调用lst.remove(val)
,列表都必须进行线性搜索,将每个元素与val
进行比较。但是您已经知道所需元素的索引(或者,您可以通过使用枚举来知道它),因此整个搜索都是浪费的;只需dellst[idx]
即可
其次,无论您是删除
还是删除
,您仍然在从数组中间删除。这意味着将所有后续元素向上移动一个插槽。它有一个快得多的常数(它只是一个大的memmove,而不是一堆对比较函数的调用),但它仍然是线性的
这是在你的内部循环中进行的。因此,您将一个额外的因数N
乘以您已经是二次的时间。通过bisect
在对数时间而不是线性时间内进行搜索的任何努力都是