【问题标题】:What is the most efficient way to find if something is repeated in a list? [duplicate]查找列表中是否重复某些内容的最有效方法是什么? [复制]
【发布时间】:2016-10-22 18:53:42
【问题描述】:

假设我有一个列表:

l = ['a', 'b', 'c', 'd', 'e', 'f', 'e']

如您所见,索引 4 和 6 是重复的。我的问题是:查看列表中是否有重复内容的最有效方法是什么?

选项 1:

output = len(set(l)) != len(l):

如果输出为假,则其中存在不止一次的值。

选项 2:

output = True
for i in l:
  if l.count(i) > 1:
    output = False

如果输出为假,则其中存在不止一次的值。

问题:

  1. 最有效的方法是什么?

  2. 如何计算这两个(或更多?)选项的 O 表示法?

谢谢!

【问题讨论】:

  • 这不是重复的,因为该问题询问“什么是最好的方式(最好是传统方式)”并且所选答案指出“不是最有效的,但直截了当和简洁” -这个问题是最有效的方法。 @米奇
  • 1.对于典型的数据类型,选项 1 在常数因子下是最优的; 2.看起来像你的家庭作业,请拿出你自己的考虑
  • 1 在我看来像 O(n),而 2 像 O(n^2)
  • 看看你目前对这个问题的 5 个答案 - 没有一个答案可以证明他们的答案是最快的,而且他们几乎都可以可以在链接的副本中找到。最好的办法是根据您使用的任何大小列表自行对它们进行基准测试,然后得出自己的结论。
  • 将其关闭为另一个帖子的欺骗。正如@Mitch 所说,如果您想要最好的,那么您当然需要进行基准测试。否则,这个问题将变成一个有意见的问题,其中每个回答者都会试图争辩说他们的答案是最好的。

标签: python list python-2.7


【解决方案1】:

这是我能想到的最简单的速度比较。

选项 1 基本上归结为从列表中创建一个集合:

(dev) go|c:\srv\tmp> python -m timeit "set(range(100))"
100000 loops, best of 3: 5.48 usec per loop

而选项 2 归结为迭代列表并进行成员资格测试(以及我正在跳过的集合添加):

(dev) go|c:\srv\tmp> python -m timeit "for i in range(100): i in set()"
10000 loops, best of 3: 22.1 usec per loop

(dev) go|c:\srv\tmp> python -m timeit "for i in range(50): i in set()"
100000 loops, best of 3: 10 usec per loop

(dev) go|c:\srv\tmp> python -m timeit "for i in range(25): i in set()"
100000 loops, best of 3: 5.2 usec per loop

所以,第一个副本必须在列表中 at most 25% 以使选项 2 更快,并且添加第二个 set 操作将进行比较选项 2 更糟糕。

【讨论】:

    【解决方案2】:

    循环并收集一组中看到的物品。

    一旦找到第一个重复项,请注意break。在极端情况下(不重复),您将循环一次列表并构建一个包含每个列表项的集合。

    l = ['a', 'b', 'c', 'd', 'e', 'f', 'e']
    seen = set()
    
    for x in l:
        if x in seen:
            print("seen '{}' already, done".format(x))
            # As soon as find find the first duplicate, break.
            break
        seen.add(x)
    

    输出:

    seen 'e' already, done
    

    【讨论】:

    • 不是一个所有值都为真的字典,为什么不是一个集合?
    • @RemcoGerlich 完成了。
    • 这将是找出重复值的好方法,但我不需要。我只是想了解是否有任何重复值,以及完成此任务最有效的方法。
    • @AbrahamB:你知道哪个值被复制只是一个副作用。这个实现的问题是它在找到重复项时会中断循环。如果你有一个一开始就有一百万个重复项的列表,那么与其他解决方案相比,这将非常有效。但是如果有一百万个没有重复的项目列表,它会很慢,因为它是一个用 Python 编写的循环。您需要用真实数据进行基准测试来决定。
    • @AbrahamB 是的,一旦找到第一个重复项,请注意break。在极端情况下(不重复),您将循环一次列表并构建一个包含每个列表项的集合。
    【解决方案3】:

    关于计算O()值:

    选项 1 做了 4 件事:创建一个集合,获取它的长度,获取列表的长度,然后比较它们。其中,创建集合必须至少为 O(n),其他最多为 O(n),因此效率以集合创建为主。我相信 Python 中集合的实现是这样的,插入平均需要 O(1),因此这应该是 O(n)

    选项 2 包含一个循环。在循环内部,您调用l.count,它遍历整个列表以计算项目出现的次数。所以每次迭代都是O(n)。循环本身为循环中的每个项目运行,所以 n 次。总效率为O(n*n)

    是否存在比选项 1 更快的东西取决于您的真实数据的特征、其长度、重复的可能性、不同项目的数量(如果它们都是小写字母,那么任何长度 > 26 的列表都有重复,检查起来真的很快)等等。无法回答。但是 O(n) 真的很难被击败,如果重复很少,那么通常所有项目都必须检查,这已经是 O(n) 了。

    【讨论】:

    • 好吧,平均而言,在尝试n/2 项之后,您会在一个只有一个重复项的列表中找到重复项。如果列表仅包含 1 个重复值,您将在索引 2 处找到重复值。所以 O(n) 不一定很难击败 ;-) Otoh,O(n) python- C-ops 和 O(n) Python ops...
    【解决方案4】:

    选项 1 很快。

    因为 set 方法使用散列,而 len 方法需要 O(1) 时间。

    因此这是任何人都能做到的最快方式。

    https://wiki.python.org/moin/TimeComplexity

    【讨论】:

    • 即使一百万个项目列表的前两个元素重复,它仍然需要构建整个集合。说它是任何人都能做到的最快也太过分了。
    • 在最坏的情况下,提前退出将不起作用@Mitch
    • 在其他一些语言中这可能不是真的,但对于 Python 这绝对是正确的答案。只需对其进行基准测试即可验证。
    【解决方案5】:

    以下是一种以良好的字典格式为您提供所有信息的解决方案:

    from collections import Counter
    
    l = ['a', 'b', 'c', 'd', 'e', 'f', 'e']
    
    # Get how many of each exist.
    counts = Counter(l)
    # Looks like this:
    # Counter({'e': 2, 'd': 1, 'c': 1, 'f': 1, 'a': 1, 'b': 1})
    
    # Print letters which have 2 or more instances.
    for i in counts:
        if counts[i] > 1:
            print(i, counts[i])
    

    一旦你有了这个,就可以在恒定时间内访问单个字典元素。尽管打印每个副本仍然需要 O(n) 时间。这以及计数器的初始成本也是 O(n),使得 O(2n) 接近于 O(n)。

    【讨论】:

    • 构建 dict 会占用更多资源,而且效率不高。
    猜你喜欢
    • 2020-03-23
    • 2020-01-09
    • 2020-01-31
    • 1970-01-01
    • 2013-07-04
    • 2010-09-16
    • 2017-08-23
    • 2017-10-01
    • 1970-01-01
    相关资源
    最近更新 更多