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


389

我需要以下功能:

输入:alist

输出

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

性能:当然,我不希望产生任何不必要的开销。

我认为最好:

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

但是我不确定最Pythonic的方法是什么。


缺少短路功能只会损害早期输入不相等的长输入(超过50个元素)。如果这种情况经常发生(频率取决于列表的长度),则需要短路。最好的短路算法似乎是@KennyTM checkEqual1。但是,它为此付出了巨大的代价:

  • 性能几乎是同类产品的20倍
  • 短名单上的性能提高了2.5倍

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


3
等于a == b或等于a is b
kennytm

1
解决方案应处理空列表吗?如果是这样,应该退还什么?
道格

1
等于a == b。应该处理空列表,并返回True。
最大

2
尽管我知道它的速度比其他一些建议慢,但我很惊讶functools.reduce(operator.eq, a)没有提出建议。
user2846495

Answers:


420

通用方法:

def checkEqual1(iterator):
    iterator = iter(iterator)
    try:
        first = next(iterator)
    except StopIteration:
        return True
    return all(first == rest for rest in iterator)

单线:

def checkEqual2(iterator):
   return len(set(iterator)) <= 1

也是单线的:

def checkEqual3(lst):
   return lst[1:] == lst[:-1]

这三个版本之间的区别在于:

  1. checkEqual2内容中必须是可哈希的。
  2. checkEqual1并且checkEqual2可以使用任何迭代器,但checkEqual3必须接受序列输入,通常是列表或元组之类的具体容器。
  3. checkEqual1 发现差异后立即停止。
  4. 由于checkEqual1包含更多的Python代码,因此当许多项目在开始时相等时效率较低。
  5. 由于checkEqual2checkEqual3始终执行O(N)复制操作,因此,如果您的大多数输入将返回False,则它们将花费更长的时间。
  6. 对于checkEqual2checkEqual3很难适应从a == b到的比较a 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 = []

我们得到

      | 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   |

注意:

# 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

1
谢谢,这对替代品确实很有帮助。您能否仔细检查一下性能表-全部以毫秒为单位,数字是否在正确的单元格中?
最大

7
@max:是的。请注意1毫秒= 1000微秒。
kennytm

1
不要忘记对非常大的数组进行内存使用情况分析,这是一种本机解决方案,可以优化对obj.__eq__when的调用lhs is rhs,以及无序优化,可以更快地将排序后的列表短路。
Glenn Maynard

3
Ivo van der Wijk提供了一种更好的解决方案,该解决方案的速度比set和内存中的O(1)快约5倍。
aaronasterling

2
itertools我还添加了一个食谱作为答案。可能值得将其放入您的时序矩阵中:-)。
mgilson

298

比对序列(不是可迭代对象)使用set()更快的解决方案是仅对第一个元素进行计数。这假设列表是非空的(但是检查起来很麻烦,并自己决定结果应该在空列表中)

x.count(x[0]) == len(x)

一些简单的基准:

>>> 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

5
天哪,这比设置的解决方案快6倍!(2.8亿个元素/秒,而我的笔记本电脑为4500万个元素/秒)。为什么???并且有任何方法可以对其进行修改,以使其短路(我想不是...)
最长

1
我猜list.count具有高度优化的C实现,并且列表的长度存储在内部,因此len()也是便宜的。没有办法缩短count(),因为您将需要真正检查所有元素以获取正确的计数。
伊万·范德·维克

我可以将其更改为:x.count(next(x)) == len(x)使其适用于任何容器x吗?嗯.. nm,刚刚看到.count仅适用于序列。.为什么不为其他内置容器实现它?在字典中进行计数本质上没有在列表中有意义吗?
最大

4
迭代器可能没有长度。例如,它可以是无限的,也可以是动态生成的。您只能通过将其转换为列表来找到它的长度,该列表会失去大多数迭代器的优势
Ivo van der Wijk 2010年

抱歉,我的意思是为什么count不对可迭代对象实现,而不是为什么len对迭代器不可用。答案可能是这只是疏忽。但这对我们来说是无聊的,因为.count()序列的默认设置非常慢(纯python)。原因您的解决方案是如此之快是它依赖于C实现的count提供list。因此,我想count用C 来实现方法的任何可迭代的事情都会从您的方法中受益。
最大

163

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

all(x==myList[0] for x in myList)

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

关于性能,这将尽早失败,因此它是渐近最佳的。


可以,但是比@KennyTM慢(1.5倍)checkEqual1。我不知道为什么。
最大

4
最多:可能是因为我没有去执行优化first=myList[0] all(x==first for x in myList),也许
ninjagecko

我认为myList [0]在每次迭代时都会进行评估。>>> timeit.timeit('all([y == x [0] for y in x])','x = [1] * 4000',number = 10000)2.707076672740641 >>> timeit.timeit('x0 = x [0]; all([y == x0中的y的x0))','x = [1] * 4000',数字= 10000)2.0908854261426484
Matt Liberty

1
我当然应该澄清一下,该优化first=myList[0]IndexError在一个空列表上抛出一个空白,因此我提到的谈论该优化的评论者将不得不处理一个空列表的边缘情况。但是,原始文件很好(在中x==myList[0]是很好的,all因为如果列表为空,则从不评估)。
ninjagecko

1
这显然是正确的方法。如果在每种情况下都希望速度,请使用numpy之类的方法。
亨利·戈默索尔

45

一组比较工作:

len(set(the_list)) == 1

使用set删除所有重复的元素。


26

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

if len(sets.Set(input_list)) == 1
// input_list has all identical elements.

这很好,但不会短路,您必须计算结果列表的长度。
aaronasterling

15
为什么不只是len(set(input_list)) == 1
Nick Dandoulakis

2
@codaddict。这意味着即使前两个元素是不同的,它仍将完成整个搜索。它还使用O(k)多余空间,其中k是列表中不同元素的数量。
aaronasterling

1
@max。因为构建集合是在C中进行的,因此实现不正确。您至少应该在生成器表达式中执行此操作。请参阅KennyTM的答案,以了解如何正确使用而不套用它。
aaronasterling

1
sets.Set为“自2.6版起不推荐使用:内置set / frozenset类型替换此模块。” (从docs.python.org/2/library/sets.html
莫伯格

21

对于它的价值,它最近出现在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)

据说它的性能非常好,并且具有一些不错的属性。

  1. 短路:一旦找到第一个不等项,它将立即停止消耗可迭代项中的项。
  2. 不需要项目是可哈希的。
  3. 它是惰性的,仅需要O(1)额外的内存来执行检查。

1换句话说,我不能因提出解决方案而功不可没-甚至找不到它也不能功劳。


3
在最坏的情况下,比这里列出的最快答案要快得多。
ChaimG '18

return next(g, f := next(g, g)) == f(当然来自py3.8)
Chris_Rands

17

这是两种简单的方法

使用set()

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

len(set(input_list))==1

这是一个例子

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

使用all()

这会将输入列表的第一个元素与列表中的所有其他元素进行比较(等效)。如果相等,则返回True,否则返回False。

all(element==input_list[0] for element in input_list)

这是一个例子

>>> 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

PS如果要检查整个列表是否等效于某个值,则可以在input_list [0]中设置该值。


1
对于对运行时感兴趣的人,执行len(set(a))10,000,000个元素的列表要花0.09 s,而执行all要花0.9 s(长10倍)。
Elliptica

2
除了@Elliptica提到的性能得分外,我也喜欢这个答案的Python简洁性
NickBraunagel

11

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

def constantList(x):
    return x and [x[0]]*len(x) == x

它比计算机上设置的解决方案慢3倍,而忽略了短路。因此,如果在列表的前三分之一中平均发现不等元素,则平均起来更快。
最多

9

这是一种简单的方法:

result = mylist and all(mylist[0] == elem for elem in mylist)

这稍微复杂一点,但会产生函数调用开销,但语义会更清楚地说明:

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

您可以通过使用来避免多余的比较for elem in mylist[1:]。值得怀疑的是,尽管自从我猜出来以来,它可以大大提高速度,elem[0] is elem[0]所以解释器可能可以非常快速地进行比较。
布伦丹


4

怀疑这是“最Python化的”,但类似:

>>> falseList = [1,2,3,4]
>>> trueList = [1, 1, 1]
>>> 
>>> def testList(list):
...   for item in list[1:]:
...     if item != list[0]:
...       return False
...   return True
... 
>>> testList(falseList)
False
>>> testList(trueList)
True

会成功的


1
for可以if any(item != list[0] for item in list[1:]): return False使用完全相同的语义使您的循环更具Pythonic 性。
musiphil

4

如果您对可读性更高(但当然不那么有效)感兴趣,可以尝试:

def compare_lists(list1, list2):
    if len(list1) != len(list2): # Weed out unequal length lists.
        return False
    for item in list1:
        if item not in list2:
            return False
    return True

a_list_1 = ['apple', 'orange', 'grape', 'pear']
a_list_2 = ['pear', 'orange', 'grape', 'apple']

b_list_1 = ['apple', 'orange', 'grape', 'pear']
b_list_2 = ['apple', 'orange', 'banana', 'pear']

c_list_1 = ['apple', 'orange', 'grape']
c_list_2 = ['grape', 'orange']

print compare_lists(a_list_1, a_list_2) # Returns True
print compare_lists(b_list_1, b_list_2) # Returns False
print compare_lists(c_list_1, c_list_2) # Returns False

我实际上是在尝试查看一个列表中的所有元素是否相同;如果两个单独的列表相同,则不是。
最大

4

将列表转换为集合,然后找到集合中的元素数。如果结果为1,则其元素相同,如果不相同,则列表中的元素不相同。

list1 = [1,1,1]
len(set(list1)) 
>1

list1 = [1,2,3]
len(set(list1)
>3

4

关于reduce()与一起使用lambda。这是一个我个人认为比其他答案更好的工作代码。

reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))

返回一个元组,如果所有项目相同或不同,则第一个值为布尔值。


编写的代码中有一个小错误(try [1, 2, 2]):它没有考虑以前的布尔值。可以通过替换x[1] == y为来解决x[0] and x[1] == y
拍摄

3

我会做:

not any((x[i] != x[i+1] for i in range(0, len(x)-1)))

因为any一旦找到True条件就停止搜索可迭代对象。


如果这是唯一的参数,则不需要在生成器表达式周围附加括号。
ninjagecko 2012年

如此all(),为什么不使用all(x == seq[0] for x in seq)呢?看起来更pythonic,应该执行相同的操作
Chen A.

2
>>> a = [1, 2, 3, 4, 5, 6]
>>> z = [(a[x], a[x+1]) for x in range(0, len(a)-1)]
>>> z
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
# Replacing it with the test
>>> z = [(a[x] == a[x+1]) for x in range(0, len(a)-1)]
>>> z
[False, False, False, False, False]
>>> if False in z : Print "All elements are not equal"

2
def allTheSame(i):
    j = itertools.groupby(i)
    for k in j: break
    for k in j: return False
    return True

在没有“ all”的Python 2.4中工作。


1
for k in j: break等同于next(j)def allTheSame(x): return len(list(itertools.groupby(x))<2)如果您不关心效率,也可以这样做。
ninjagecko 2012年

2

可以使用地图和lambda

lst = [1,1,1,1,1,1,1,1,1]

print all(map(lambda x: x == lst[0], lst[1:]))

2

或使用diffnumpy的方法:

import numpy as np
def allthesame(l):
    return np.all(np.diff(l)==0)

并致电:

print(allthesame([1,1,1]))

输出:

True

我认为not np.any(np.diff(l))可能会更快。
GZ0

2

或者使用numpy的diff方法:

import numpy as np
def allthesame(l):
    return np.unique(l).shape[0]<=1

并致电:

print(allthesame([1,1,1]))

输出:

真正


该答案与去年的U9-Forward的答案相同。
mhwombat

好眼睛!我使用了相同的结构/ API,但是我的方法使用了np.unique和shape。U9的函数使用np.all()和np.diff()-我不使用任何一个函数。
路易斯B

1

你可以做:

reduce(and_, (x==yourList[0] for x in yourList), True)

python使您像那样导入运算符是很烦人的operator.and_。从python3开始,您还需要import functools.reduce

(您不应该使用此方法,因为如果找到不相等的值,它将不会中断,但是会继续检查整个列表。此处仅作为完整性的答案。)


这不会短路。为什么比其他解决方案更喜欢它?
最大

@max:正是因为这个原因,您不会;为了完整起见,我将其包括在内。我可能应该对其进行编辑以提及,谢谢。
ninjagecko 2012年

1
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]

下一个将短路短路:

all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))

您的第一个代码显然是错误的:reduce(lambda a,b:a==b, [2,2,2])yields False...我对其进行了编辑,但是这样就不再那么漂亮了
berdario

@berdario然后,您应该编写自己的答案,而不是更改其他人的内容。如果您认为此答案有误,则可以对其进行评论和/或对其进行否决。
Gorpik 2014年

3
修复错误要好于让所有人都阅读它,可能遗漏了解释错误原因的注释
berdario

3
“我应该何时编辑帖子?” “任何时候只要您觉得可以使该帖子变得更好,并且愿意这样做,就可以鼓励编辑!”
berdario 2014年

1

将列表更改为一组。然后,如果集合的大小仅为1,则它们必须相同。

if len(set(my_list)) == 1:

1

还有一个纯Python递归选项:

 def checkEqual(lst):
    if len(lst)==2 :
        return lst[0]==lst[1]
    else:
        return lst[0]==lst[1] and checkEqual(lst[1:])

但是由于某种原因,它在某些情况下比其他选择要慢两个数量级。从C语言的心态来看,我期望这会更快,但事实并非如此!

另一个缺点是Python中存在递归限制,在这种情况下需要对其进行调整。例如使用this


0

您可以.nunique()用来查找列表中唯一项目的数量。

def identical_elements(list):
    series = pd.Series(list)
    if series.nunique() == 1: identical = True
    else:  identical = False
    return identical



identical_elements(['a', 'a'])
Out[427]: True

identical_elements(['a', 'b'])
Out[428]: False

0

您可以使用set。它将设置并删除重复的元素。然后检查其元素是否超过1个。

if len(set(your_list)) <= 1:
    print('all ements are equal')

例:

>>> len(set([5, 5])) <= 1
True
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.