更快地比较两个列表

时间:2018-07-17 21:21:07

标签: 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_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()

本质上,将两者进行比较的部分列出了它们是否匹配后对其进行排序的操作,然后它将执行操作以获取增量并将其从副本中删除,然后使旧副本等于副本,从而使其不等于副本遍历列表时删除项目。如果该项目不匹配,则表示该项目不在旧文件中,而是在新文件中。

我想要一些可能使此过程更快的东西。

4 个答案:

答案 0 :(得分:3)

这里有很多代码,而且缩进显然是不正确的,所以我什至不知道逻辑到底应该是什么,也没有迹象表明您认为哪一部分很慢(或者您怎么知道),但是一件事立即跳了出来:

compare_list_copy.remove(line)

…和另一个remove

首先,每当您调用lst.remove(val)时,列表都必须进行线性搜索,将每个元素与val进行比较。但是您已经知道想要的元素的索引(或者,仅使用enumerate,您就可以知道它们的索引),因此浪费了整个搜索时间;只是del lst[idx]

第二,无论您是remove还是del,您仍在从数组中间删除。这意味着将所有后续元素上移一个插槽。它有一个快得多的常量(这只是一个很大的记忆,而不是一堆对比较函数的调用),但是它仍然是线性的。

您正在内部循环中执行此操作。因此,您正在将N的额外因数乘以已经是二次的时间。如果您只是在对数搜索中对相同数据进行线性搜索,那么您浪费大量精力进行对数搜索而不是通过bisect进行线性时间搜索就会浪费掉。


如果您需要某些内容,可以在对数时间进行搜索,也可以在对数时间进行修改,则需要的是某种树(或树列表结构,例如跳过列表)。有很好的库在PyPI上包装了各种二叉树和b树变体,或者您可以在Wikipedia上查找算法。

或者,您也可以像Sorted Containers库之类的东西来抓取更高级别的内容。例如,sorteddict的行为与dict很相似,但是您可以搜索最接近的键而不是完全匹配的键,也可以搜索给定范围内的所有键,等等。带有某种混合式的斜拉绳之类的东西,但您无需关心这些细节;重要的是它可以保证您在对数时间内所需的所有操作。


完成此操作后,两个外循环中的至少一个也可以从对数搜索中获利(使用树几乎可以免费获得对数搜索)。

此时您的总时间为O(log**2 N * N),而不是O(N**3),这是一个巨大的差异。

如果您不习惯用算法复杂度来处理性能,请考虑以下问题:只有1000个元素,立方时间需要1000*1000*1000 = 10亿步;对数平方线性时间为10*10*1000 = 10万步。那是几天和几秒钟之间的区别。

答案 1 :(得分:1)

您的当前比较至少是二次的(由于嵌套循环)。从第一个列表(线性时间)生成字典的速度更快,在第一个列表中,键是名称的元组和前两个坐标(对于新文件和旧文件,它们似乎相同),然后对于在第二个列表中,检查该键是否在字典中(再次是线性时间)。

答案 2 :(得分:0)

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

想法是用键和值将旧列表构造为字典。 这样,我们就不会相对于第一个列表重复第二个列表。

答案 3 :(得分:-1)

您是否考虑过使用difflib库来解决您的问题?

如果您能尝试一下,我期待听到表演。