【问题标题】:Fastest nested loops over a single list (with elements remove or not)单个列表上最快的嵌套循环(是否删除元素)
【发布时间】:2010-12-07 11:27:20
【问题描述】:

我正在寻找有关如何以最快的方式解析单个列表、使用两个嵌套循环、避免进行 len(list)^2 比较以及避免组中重复文件的建议。

更准确地说:我有一个“文件”对象列表,每个对象都有一个时间戳。我想按文件的时间戳和时间偏移对文件进行分组。前任。从文件 X 开始,我想创建一个包含所有具有 timestamp < (timestamp(x) + offset) 的文件的组。

为此,我做到了:

for file_a in list:
   temp_group = group()
   temp_group.add(file_a)
   list.remove(file_a)
   for file_b in list:
      if (file_b.timestamp < (file_a.timestamp + offset)):
         temp_group.add(file_b)
         list.remove(file_b)

   groups.add(temp_group)

(好吧,代码比较复杂,但这是主要思想)

这显然是行不通的,因为我在循环期间修改列表,奇怪的事情发生了:)

我认为我必须为循环使用“列表”的副本,但是,这也不起作用:

for file_a in list[:]:
   temp_group = group()
   temp_group.add(file_a)
   list.remove(file_a)
   for file_b in list[:]:
      if (file_b.timestamp < (file_a.timestamp + offset)):
         temp_group.add(file_b)
         list.remove(file_b)

   groups.add(temp_group)

嗯.. 我知道我可以在不从列表中删除元素的情况下做到这一点,但是我需要标记那些已经“处理”过的元素,并且我需要每次都检查它们 - 这是一个速度损失。

谁能给我一些建议,告诉我如何以最快/最好的方式做到这一点?

谢谢,

亚历克斯

编辑:我找到了另一个解决方案,它不能完全回答问题,但它是我真正需要的(我以这种方式提出问题的错误)。我在此处发布此内容是因为它可以帮助人们在 Python 中寻找与列表循环相关的问题。

它可能不是最快的(考虑到列表中“通过”的次数),但它很容易理解和实现,并且不需要对列表进行排序。

我避免排序的原因是它可能需要更多时间,因为在我制作了第一组组后,其中一些会被“锁定”,而未锁定的组将被“解散”,并且使用不同的时间偏移重新组合。 (并且解散组时,文件顺序可能会改变,需要重新排序)。

无论如何,解决方案是自己控制循环索引。如果我从列表中删除一个文件,我会跳过增加索引(例如:当我删除索引“3”时,之前的索引“4”现在是“3”,我不想增加循环计数器,因为我会跳过它)。如果在那次迭代中我没有删除任何项目,那么索引会正常增加。这是代码(有一些额外的东西;忽略所有那些“桶”的东西):

def regroup(self, time_offset):
    #create list of files to be used for regrouping
    regroup_files_list = []

    if len(self.groups) == 0:
        #on first 'regroup', we start with a copy of jpeg_list, so that we do not change it further on
        regroup_files_list = copy.copy(self.jpeg_list) 

    else:
        i = 0
        while True:
            try:
                group = self.groups[i]
            except IndexError:
                break

            if group.is_locked == False:
                regroup_files_list.extend(group)                    
                self.groups.remove(group)
                continue
            else:
                i += 1

    bucket_group = FilesGroup()
    bucket_group.name = c_bucket_group_name

    while len(regroup_files_list) > 0: #we create groups until there are no files left
        file_a = regroup_files_list[0]
        regroup_files_list.remove(file_a)

        temp_group = FilesGroup()
        temp_group.start_time = file_a._iso_time
        temp_group.add(file_a)

        #manually manage the list index when iterating for file_b, because we're removing files
        i = 0

        while True:
            try:
                file_b = regroup_files_list[i]
            except IndexError:
                break

            timediff = file_a._iso_time - file_b._iso_time              
            if timediff.days < 0 or timediff.seconds < 0:
                timediff = file_b._iso_time - file_a._iso_time

            if timediff < time_offset:
                temp_group.add(file_b)
                regroup_files_list.remove(file_b)
                continue # :D we reuse the old position, because all elements were shifted to the left

            else:
                i += 1 #the index is increased normally

        self.groups.append(temp_group)

        #move files to the bucket group, if the temp group is too small
        if c_bucket_group_enabled == True:                    
            if len(temp_group) < c_bucket_group_min_count:
                for file in temp_group:
                    bucket_group.add(file)
                    temp_group.remove(file)    
            else:
                self.groups.append(temp_group)      

    if len(bucket_group) > 0:
        self.groups.append(bucket_group)

【问题讨论】:

    标签: python performance list nested-loops


    【解决方案1】:

    通过对列表进行排序然后使用生成器创建组的简单解决方案:

    def time_offsets(files, offset):
    
       files = sorted(files, key=lambda x:x.timestamp)
    
       group = []   
       timestamp = 0
    
       for f in files:
          if f.timestamp < timestamp + offset:
             group.append(f)
          else:
             yield group
             timestamp = f.timestamp
             group = [timestamp]
       else:
          yield group
    
    # Now you can do this...
    for group in time_offsets(files, 86400):
       print group
    

    这是一个完整的脚本,您可以运行它来测试:

    class File:
       def __init__(self, timestamp):
          self.timestamp = timestamp
    
       def __repr__(self):
          return "File: <%d>" % self.timestamp
    
    def gen_files(num=100):
       import random
       files = []
       for i in range(num):
          timestamp = random.randint(0,1000000)
          files.append(File(timestamp))
    
       return files
          
    
    def time_offsets(files, offset):
    
       files = sorted(files, key=lambda x:x.timestamp)
    
       group = []   
       timestamp = 0
    
       for f in files:
          if f.timestamp < timestamp + offset:
             group.append(f)
          else:
             yield group
             timestamp = f.timestamp
             group = [timestamp]
       else:
          yield group
    
    # Now you can do this to group files by day (assuming timestamp in seconds)
    files = gen_files()
    for group in time_offsets(files, 86400):
       print group
    

    【讨论】:

    • 谢谢!我已经对此进行了测试,它与我需要的非常接近。我必须看看生成器(不确定 'yield' 是做什么的),而 lambda 函数语法对我来说仍然是新的。感谢您提供完整的示例:)
    【解决方案2】:

    我能想到的最佳解决方案是O(n log n)

    listA = getListOfFiles()
    listB = stableMergesort(listA, lambda el: el.timestamp)
    listC = groupAdjacentElementsByTimestampRange(listB, offset)
    

    请注意,groupAdjacentElementsByTimestampRangeO(n)

    【讨论】:

    • 是的 - 几乎就是我所做的。
    • 是的,这正是你所做的!
    【解决方案3】:

    我不确定您要做什么 - 在我看来,列表的顺序会影响分组,但您现有的代码可以修改为这样工作。

    #This is O(n^2)
    while lst:
        file_a=lst.pop()
        temp_group = group()
        temp_group.add(file_a)
        while lst
            file_b=lst[-1] 
            if (file_b.timestamp < (file_a.timestamp + offset)):
                temp_group.add(lst.pop())
        groups.add(temp_group)
    

    组必须从 file_a.timestamp 开始吗?

    # This is O(n)
    from collections import defaultdict
    groups=defaultdict(list)  # This is why you shouldn't use `list` as a variable name
    for item in lst:
        groups[item.timestamp/offset].append(item)
    

    更简单的方法是分割成具有相似时间戳的组

    【讨论】:

    • 感谢您提供两个代码 sn-ps!第二个似乎很有趣,但我有 python 2.6.2 (linux),显然我没有'defaultdict' 可用了。我将尝试调整代码并看看它是如何运行的,因为现在我不确定它是如何工作的(我是 Python 新手)。谢谢!
    • 对不起,我的大脑f*rt。 defaultdict 来自集合。它肯定是在 2.6
    猜你喜欢
    • 1970-01-01
    • 2014-05-03
    • 2011-07-14
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-17
    相关资源
    最近更新 更多