проверьте, идентичны ли все элементы в списке

мне нужна следующая функция:

вход: a list

выход:

  • True если все элементы во входном списке оцениваются как равные друг другу с помощью стандартного оператора равенства;
  • False иначе.

производительность: конечно, я предпочитаю не нести лишних издержек.

Я чувствую, что было бы лучше:

  • повторите список
  • сравниваем соседние элементы
  • и AND все результирующие логические значения

но я не уверен, что наиболее подходящие для Python способ сделать это.


редактировать:

Спасибо за все отличные ответы. Я оценил несколько, и было очень трудно выбрать между @KennyTM и @Ivo van der Wijk solutions.

отсутствие короткого замыкания функция только болит на длинном входе (более ~50 элементов), которые имеют неравные элементы в начале. Если это происходит достаточно часто (как часто зависит от того, как долго могут быть списки), требуется короткое замыкание. Лучшим алгоритмом короткого замыкания кажется @KennyTM checkEqual1. Он платит, однако, значительную стоимость за это:

  • до 20x в производительности почти идентичные списки
  • до 2.5 x в производительности в коротких списках

если долго входов с ранними неравными элементами не происходит (или происходит достаточно редко), короткое замыкание не требуется. Тогда, безусловно, самым быстрым является решение @Ivo van der Wijk.

21 ответов


общий метод:

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]

разница между 3 версиями заключается в том, что:

  1. на checkEqual2 контент должен быть hashable.
  2. checkEqual1 и checkEqual2 можно использовать любые итераторы, но checkEqual3 должен принимать ввод последовательности, обычно конкретные контейнеры, такие как список или кортеж.
  3. checkEqual1 останавливается, как только разница найдено.
  4. С checkEqual1 содержит больше кода Python, он менее эффективен, когда многие из элементов равны в начале.
  5. С checkEqual2 и checkEqual3 всегда выполняйте O (N) операции копирования, они будут занимать больше времени, если большая часть вашего ввода вернет False.
  6. на checkEqual2 и checkEqual3 труднее адаптировать сравнение из a == b to 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

решение быстрее, чем использование set (), которое работает на последовательностях (не iterables), - это просто подсчитать первый элемент. Это предполагает, что список не пуст (но это тривиально проверить и решить самостоятельно, какой результат должен быть в пустом списке)

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

самый простой и элегантный способ заключается в следующем:

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

(Да, это даже работает с нулевым списком! Это потому, что это один из немногих случаев, когда Python имеет ленивую семантику.)

Что касается производительности, это произойдет как можно скорее, поэтому оно асимптотически оптимально.


работа сравнения набора:

len(set(the_list)) == 1

используя set удаляет все повторяющиеся элементы.


вы можете преобразовать список в набор. Набор не может иметь дубликатов. Поэтому, если все элементы в исходном списке идентичны, набор будет иметь только один элемент.

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

для чего это стоит, это придумали на python-список рассылки идей недавно. Оказывается, есть рецепт 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. короткие замыкания: он перестанет потреблять элементы из iterable, как только найдет первый неравный элемент.
  2. не требует элементов hashable.
  3. это лениво и требует только O(1) дополнительной памяти для проверки.

1другими словами, Я не могу взять на себя ответственность за то, что придумал решение - и я не могу взять на себя ответственность даже за найти его.


Это еще один вариант, быстрее, чем len(set(x))==1 для длинных списков (использует короткое замыкание)

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

это простой способ сделать это:

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)

сомневаюсь, что это "самый Питонский", но что-то вроде:

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

будет делать трюк.


Если вас интересует что-то более читаемое (но, конечно, не столь эффективное), вы можете попробовать:

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

Я бы сделал:

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

as any прекращает поиск iterable, как только он находит True состояние.


о reduce() С lambda. Вот рабочий код, который я лично считаю более приятным, чем некоторые другие ответы.

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

возвращает truple, где первое значение является логическим, если все элементы одинаковы или нет.


проверьте, все ли элементы равны первому.

np.allclose(array, array[0])


преобразовать список в набор, а затем найти количество элементов в наборе. Если результат равен 1, он имеет идентичные элементы, а если нет, то элементы в списке не идентичны.

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

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

вот два простых способа сделать это

С помощью 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

P. S Если вы проверяете, эквивалентен ли весь список определенному значению, вы можете suibstitue значение для input_list[0].


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

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

работает в Python 2.4, который не имеет "все".


можно использовать map и lambda

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

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

вы можете сделать:

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

Это довольно раздражает, что python заставляет вас импортировать операторы, такие как operator.and_. Начиная с python3, вам также нужно будет импортировать functools.reduce.

(вы не должны использовать этот метод, потому что он не сломается, если найдет неравные значения, но продолжит изучение всего списка. Он просто включен здесь в качестве ответа для полноты.)


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

измените список на набор. Тогда, если размер набора равен только 1, они должны быть одинаковыми.

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