检查列表中的所有元素是否相同

max*_*max 351 python algorithm comparison

我需要以下功能:

输入:alist

输出:

  • True 如果输入列表中的所有元素使用标准相等运算符评估为彼此相等;
  • False 除此以外.

性能:当然,我不希望招致任何不必要的开销.

我觉得最好是:

  • 遍历列表
  • 比较相邻元素
  • 以及AND所有结果布尔值

但我不确定最恐怖的方式是什么.


编辑:

谢谢你所有的好答案.我评价了几个,而且很难在@KennyTM和@Ivo van der Wijk解决方案之间做出选择.

缺少短路功能只会对早期具有不相等元素的长输入(超过约50个元素)造成伤害.如果这种情况经常发生(通常取决于列表的长度),则需要进行短路.最好的短路算法似乎是@KennyTM checkEqual1.然而,它为此付出了巨大的代价:

  • 高达20倍的性能几乎相同的列表
  • 短名单上的表现高达2.5倍

如果早期不等元素的长输入没有发生(或很少发生),则不需要短路.然后,到目前为止最快的是@Ivo van der Wijk解决方案.

ken*_*ytm 378

一般方法:

def checkEqual1(iterator):
    iterator = iter(iterator)
    try:
        first = next(iterator)
    except StopIteration:
        return True
    return all(first == rest for rest in iterator)
Run Code Online (Sandbox Code Playgroud)

一内胆:

def checkEqual2(iterator):
   return len(set(iterator)) <= 1
Run Code Online (Sandbox Code Playgroud)

单行:

def checkEqual3(lst):
   return lst[1:] == lst[:-1]
Run Code Online (Sandbox Code Playgroud)

3个版本之间的区别在于:

  1. checkEqual2内容必须是可以清洗的.
  2. checkEqual1并且checkEqual2可以使用任何迭代器,但checkEqual3必须采用序列输入,通常是具体的容器,如列表或元组.
  3. checkEqual1 一发现差异就会停止.
  4. 由于checkEqual1包含更多Python代码,因此在开始时许多项目相同时效率较低.
  5. 由于checkEqual2并且checkEqual3始终执行O(N)复制操作,如果大多数输入将返回False,则它们将花费更长时间.
  6. 对于checkEqual2checkEqual3它很难适应从比较a == ba is b.

timeit 结果,对于Python 2.7和(只有s1,s4,s7,s9应该返回True)

s1 = [1] * 5000
s2 = [1] * 4999 + [2]
s3 = [2] + [1]*4999
s4 = [set([9])] * 5000
s5 = [set([9])] * 4999 + [set([10])]
s6 = [set([10])] + [set([9])] * 4999
s7 = [1,1]
s8 = [1,2]
s9 = []
Run Code Online (Sandbox Code Playgroud)

我们得到

      | checkEqual1 | checkEqual2 | checkEqual3  | checkEqualIvo | checkEqual6502 |
|-----|-------------|-------------|--------------|---------------|----------------|
| s1  | 1.19   msec | 348    usec | 183     usec | 51.6    usec  | 121     usec   |
| s2  | 1.17   msec | 376    usec | 185     usec | 50.9    usec  | 118     usec   |
| s3  | 4.17   usec | 348    usec | 120     usec | 264     usec  | 61.3    usec   |
|     |             |             |              |               |                |
| s4  | 1.73   msec |             | 182     usec | 50.5    usec  | 121     usec   |
| s5  | 1.71   msec |             | 181     usec | 50.6    usec  | 125     usec   |
| s6  | 4.29   usec |             | 122     usec | 423     usec  | 61.1    usec   |
|     |             |             |              |               |                |
| s7  | 3.1    usec | 1.4    usec | 1.24    usec | 0.932   usec  | 1.92    usec   |
| s8  | 4.07   usec | 1.54   usec | 1.28    usec | 0.997   usec  | 1.79    usec   |
| s9  | 5.91   usec | 1.25   usec | 0.749   usec | 0.407   usec  | 0.386   usec   |
Run Code Online (Sandbox Code Playgroud)

注意:

# http://stackoverflow.com/q/3844948/
def checkEqualIvo(lst):
    return not lst or lst.count(lst[0]) == len(lst)

# http://stackoverflow.com/q/3844931/
def checkEqual6502(lst):
    return not lst or [lst[0]]*len(lst) == lst
Run Code Online (Sandbox Code Playgroud)

  • @max:是的.请注意,1毫秒= 1000 usec. (7认同)
  • @ChaimG,如果您单击“编辑”,代码将隐藏在答案文本的注释中。 (5认同)
  • 不要忘记对非常大的数组进行内存使用分析,当`lhs 是rhs` 时优化掉对`obj.__eq__` 的调用的本机解决方案,以及允许更快地短路排序列表的乱序优化。 (4认同)
  • Ivo van der Wijk有一个更好的解决方案,比序列快5倍,内存中O(1). (2认同)
  • 我还添加了一个“ itertools”食谱作为答案。可能值得将其放入您的时序矩阵中:-)。 (2认同)
  • 如果 checkEqual3 的直觉对其他人来说不是很明显:如果 `first == secondary and secondary == Third and ...`,所有项目都是相同的 (2认同)
  • @Boris:这些图表的代码是什么? (2认同)

Ivo*_*ijk 273

比使用在序列(而不是迭代)上工作的set()更快的解决方案是简单地计算第一个元素.这假定列表是非空的(但这很容易检查,并确定自己在空列表中应该是什么结果)

x.count(x[0]) == len(x)
Run Code Online (Sandbox Code Playgroud)

一些简单的基准:

>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*5000', number=10000)
1.4383411407470703
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*4999+[2]', number=10000)
1.4765670299530029
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*5000', number=10000)
0.26274609565734863
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*4999+[2]', number=10000)
0.25654196739196777
Run Code Online (Sandbox Code Playgroud)

  • OMG,这比设定的解决方案快6倍!(我的笔记本电脑每秒2.8亿元/秒,相当于4500万元/秒).为什么???有没有办法修改它,以便它短路(我猜不是......) (4认同)
  • 迭代器可能没有长度.例如,它可以是无限的,也可以只是动态生成 您只能通过将其转换为一个列表来查找其长度,该列表会消除大多数迭代器的优点 (4认同)
  • 我猜 list.count 有一个高度优化的 C 实现,并且列表的长度是内部存储的,所以 len() 也很便宜。没有办法短路 count() ,因为您需要真正检查所有元素以获得正确的计数。 (3认同)

nin*_*cko 140

最简单,最优雅的方式如下:

all(x==myList[0] for x in myList)
Run Code Online (Sandbox Code Playgroud)

(是的,这甚至适用于空列表!这是因为这是python具有惰性语义的少数情况之一.)

关于性能,这将在尽可能早的时候失败,因此它是渐近最优的.

  • max:可能是因为我没有费心去执行优化`first = myList [0]``all(x == my in x in myList)` (3认同)
  • 我当然应该澄清优化 `first=myList[0]` 将在空列表上抛出 `IndexError`,因此谈论我提到的优化的评论者将不得不处理空列表的边缘情况. 然而,原始的很好(`x==myList[0]` 在 `all` 中很好,因为如果列表为空,它永远不会被评估)。 (3认同)

cba*_*wat 42

一组比较工作:

len(set(the_list)) == 1
Run Code Online (Sandbox Code Playgroud)

使用set删除所有重复元素.


cod*_*ict 25

您可以将列表转换为集合.一套不能有重复.因此,如果原始列表中的所有元素都相同,则该集合将只有一个元素.

if len(sets.Set(input_list)) == 1
// input_list has all identical elements.
Run Code Online (Sandbox Code Playgroud)

  • 为什么不只是`len(set(input_list))== 1`? (14认同)
  • 如果列表为空,则返回“False”,而此时应该返回“True”。它还要求所有元素都是可散列的。 (3认同)
  • @codaddict.这意味着即使前两个元素是不同的,它仍将完成整个搜索.它还使用O(k)额外空间,其中k是列表中不同元素的数量. (2认同)

mgi*_*son 18

为了它的价值,最近在python-ideas邮件列表中提出了这个问题.事实证明,有一个itertools配方已经做到这一点:1

def all_equal(iterable):
    "Returns True if all the elements are equal to each other"
    g = groupby(iterable)
    return next(g, True) and not next(g, False)
Run Code Online (Sandbox Code Playgroud)

据说它表现得非常好,并且有一些不错的属性.

  1. 短路:一旦发现第一个不相等的项目,它将停止从迭代中消耗项目.
  2. 不要求物品可以清洗.
  3. 它是懒惰的,只需要O(1)额外的内存来进行检查.

1换句话说,我不能居功能想出解决方案-我也不能采取信贷,甚至找到它.

  • 在最坏的情况下,比[此处](/sf/answers/269138271/)列出的最快答案要快得多。 (2认同)

Chr*_*cio 16

这有两个简单的方法

使用set()

将列表转换为集合时,将删除重复的元素.因此,如果转换集的长度为1,则表示所有元素都相同.

len(set(input_list))==1
Run Code Online (Sandbox Code Playgroud)

这是一个例子

>>> a = ['not', 'the', 'same']
>>> b = ['same', 'same', 'same']
>>> len(set(a))==1  # == 3
False
>>> len(set(b))==1  # == 1
True
Run Code Online (Sandbox Code Playgroud)

使用全部()

这将比较(等价)输入列表的第一个元素与列表中的每个其他元素.如果全部等效,则返回True,否则返回False.

all(element==input_list[0] for element in input_list)
Run Code Online (Sandbox Code Playgroud)

这是一个例子

>>> a = [1, 2, 3, 4, 5]
>>> b = [1, 1, 1, 1, 1]
>>> all(number==a[0] for number in a)
False
>>> all(number==b[0] for number in b)
True
Run Code Online (Sandbox Code Playgroud)

PS如果要检查整个列表是否等于某个值,则可以为input_list [0]指定值.

  • 除了@Elliptica提到的性能分数之外,我也喜欢这个pythonic简单的答案 (2认同)

650*_*502 11

这是另一种选择,比len(set(x))==1长列表更快(使用短路)

def constantList(x):
    return x and [x[0]]*len(x) == x
Run Code Online (Sandbox Code Playgroud)


Jer*_*rub 9

这是一种简单的方法:

result = mylist and all(mylist[0] == elem for elem in mylist)
Run Code Online (Sandbox Code Playgroud)

这稍微复杂一点,它会产生函数调用开销,但语义更明确:

def all_identical(seq):
    if not seq:
        # empty list is False.
        return False
    first = seq[0]
    return all(first == elem for elem in seq)
Run Code Online (Sandbox Code Playgroud)


Gus*_*ava 5

检查所有元素是否等于第一个。

np.allclose(array, array[0])