проверить, все ли элементы в списке идентичны - PullRequest
332 голосов
/ 02 октября 2010

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

Вход : a list

Выход :

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

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

Я чувствую, что это будетлучше всего:

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

Но я не уверен, какой самый Pythonic способ сделать это.


РЕДАКТИРОВАТЬ :

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

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

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

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

Ответы [ 25 ]

358 голосов
/ 02 октября 2010

Общий метод:

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

One-вкладыш:

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

Также однострочник:

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

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

  1. В checkEqual2 содержимое должно быть хэшируемым.
  2. checkEqual1 и checkEqual2 могут использовать любые итераторы, но checkEqual3 должен принимать последовательность ввода, обычно это конкретные контейнеры, такие как список или кортеж.
  3. checkEqual1 останавливается, как только обнаруживается разница.
  4. Поскольку checkEqual1 содержит больше кода Python, он менее эффективен, когда многие элементы равны в начале.
  5. Поскольку checkEqual2 и checkEqual3 всегда выполняют O (N) операции копирования, они будут занимать больше времени, если большая часть вашего ввода вернет False.
  6. Для checkEqual2 и checkEqual3 сложнее адаптировать сравнение от 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
262 голосов
/ 02 октября 2010

Решение, более быстрое, чем использование 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
126 голосов
/ 23 апреля 2012

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

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

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

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

38 голосов
/ 02 мая 2014

Сравнительная работа набора:

len(set(the_list)) == 1

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

24 голосов
/ 02 октября 2010

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

if len(sets.Set(input_list)) == 1
// input_list has all identical elements.
15 голосов
/ 06 октября 2016

Как бы то ни было, это недавно появилось в списке рассылки 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 Другими словами, я не могу взять кредит за то, что нашел решение, и при этом я не могу взять кредит даже за нахождение его. *

14 голосов
/ 24 июля 2018

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

с использованием 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

используя все ()

Это будет сравнивать (эквивалентность) первого элемента списка ввода с каждым другим элементом в списке. Если все эквивалентны, 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. Если вы проверяете, является ли весь список эквивалентным определенному значению, вы можете установить значение в для input_list [0].

10 голосов
/ 02 октября 2010

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

def constantList(x):
    return x and [x[0]]*len(x) == x
7 голосов
/ 02 октября 2010

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

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)
4 голосов
/ 05 июня 2012

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

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...