【问题标题】:The best way recognizing similarity of list lengths识别列表长度相似性的最佳方法
【发布时间】:2011-12-11 04:53:11
【问题描述】:

我有一个 dict 在其键下包含列表:

dct = {'a': [1, 2, 3],
       'b': [1, 2, 3, 4],
       'c': [1, 2]}

识别列表长度是否相同的最佳方法是什么?

这是我的解决方案:

import itertools
len(set(itertools.imap(len, dct.viewvalues()))) == 1

True 如果相似,False 如果不是

UPD:参考@RaymondHettinger 的建议,将map 替换为itertools.imap

【问题讨论】:

  • 澄清一下:你的意思是相似还是相同?
  • 我认为您的意思是 values 而不是 viewvalues
  • @StevenRumbalski 就我而言,根据python docs,使用viewvaluesvaluespythonic
  • @klesh 没有意识到 values 在 2.7 中的名称为 viewvalues。在 Python 3 中 values 返回一个视图对象并且没有 viewvalues 方法。在 2.7 之前,如果您想避免创建中间列表,您可以使用 itervalues

标签: python algorithm list


【解决方案1】:

您的解决方案看起来不错。

如果您想稍微调整一下,请使用 itertools.imap() 而不是 map()。这会将内存占用压缩到 O(1) 而不是 O(n)。

【讨论】:

  • 你对下面的 generator expression 的解决方案有什么看法?
  • genexp 也不错,它提供与 imap() 相同的内存节省优势,但 map/imap 运行得更快,因为 len() 的全局查找只完成一次。
  • O(1) 怎么样?如果所有 n 个列表的长度不同,则该集合使用 O(n) 内存。它可以通过结合 imap、groupby 和 islice 在 O(1) 空间中完成。
  • @Michael 我添加了另一个使用imapgroupby 的答案。请检查一下,如果你喜欢。我不知道,你是什么意思使用islice
【解决方案2】:

注意:ovgolovin's solution 更好。我把这个答案留在这里是因为有讨论提到它。

您的解决方案很好,但您可以使用使用更少内存且更具可读性的生成器表达式:

len(set(len(x) for x in dct.viewvalues()))) == 1

【讨论】:

    【解决方案3】:

    首先,我会坚持使用itervalues,它使用简单的评估。

    其次,我会警惕依赖使用set,因为它会在每次遍历字典时查找集合中的值。根据docs,它是超额的O(1)(如果所有长度都相同,则O(n) 在我们的情况下是O(1),如果所有长度不同,则为O(n))。但是很难评估使用 set 的开销。

    在这种情况下,我会使用allall 在找到第一个 False 值时失败。因此,长度的第一个不匹配将停止交互过程。而如果使用set,它将遍历所有列表到最后,然后才将其长度与1进行比较。

    >>> dct = {'a': [1, 2, 3],
           'b': [1, 2, 3, 4],
           'c': [1, 2]}
    >>> lenght_1 = len(dct.itervalues().next())
    >>> all(len(value)==lenght_1 for value in dct.itervalues())
    False
    
    >>> dct = {'a': [1, 2, 3],
           'b': [1, 2, 4],
           'c': [1, 2, 5]}
    >>> lenght_1 = len(dct.itervalues().next())
    >>> all(len(value)==lenght_1 for value in dct.itervalues())
    True
    

    代码可以通过使用相同的迭代器it进行优化,它不会两次遍历第一个值:

    >>> it = dct.itervalues()
    >>> length_1 = len(next(it))
    >>> all(len(value)==l1 for value in it)
    True
    

    【讨论】:

      【解决方案4】:

      正如 Michael J. Barber 在 cmets 中向 the answer 建议的那样,这里是使用 itertools 模块中的 groupbyimap 的代码。

      imap 只是将len 函数应用于每个列表。

      groupby 只是在相同长度的块中摸索值。

      因此,如果有多个长度块,则长度是不同的。如果只有一个长度的chuck,这意味着列表的长度相同,第二次访问groupby迭代器应该产生StopIteration从而返回Nonenext的默认值函数)。

      这段代码的最大好处是imapgroupby 是用C 编写的,而且速度非常快。

      from itertools import imap,groupby
      
      dct = {'a': [1, 2, 3],
             'b': [1, 2, 3, 4],
             'c': [1, 2]}
      
      dct2 = {'a': [1, 2, 3],
             'b': [1, 2, 34],
             'c': [1, 2, 5]}
      
      def check_lenghts(iterable):
          it = groupby(imap(len,iterable.itervalues()))
          next(it,None)
          return True if next(it,None)==None else False
      
      print(check_lenghts(dct))
      print(check_lenghts(dct2))
      

      【讨论】:

      • 为什么我提到islice:您可以使用islicegroupby 创建的迭代器限制为最多两个长度。在这里,您可以将check_lengths 中的next 调用替换为len(list(islice(it, 2)) < 2。总的来说,我认为您的其他答案可能更清楚。
      • @MichaelJ.Barber 另一方面,groupby 将在产生第二个值之前遍历第二个长度的所有值(如果列表具有长度为 1 的第一个值并且剩下的说 100 万个长度为 2 的值,groupby 将传递所有长度为 2 的值。但是在另一个答案中使用 all 函数只需访问长度为 2 列表的第一个值即可返回 @ 987654346@.
      • @MichaelJ.Barber 我仔细查看了 groupby 纯 Python 等效项。在达到新的key 值的那一刻,该值由groupby 产生。因此,如果真正的 C 代码具有相同的行为,则不会传递到第二个长度块的末尾,从而达到与 all 版本相同的效率。
      • 你可以在解释器中尝试一下:它确实像纯 Python 版本一样工作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-14
      • 2013-12-22
      • 1970-01-01
      • 2015-09-27
      • 2014-06-02
      • 2023-03-06
      相关资源
      最近更新 更多