Python 更快地比较两个列表

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']]

我试图对两个文件进行比较,这些文件大约有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_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
在对数时间而不是线性时间内进行搜索的任何努力都是