Вопрос-Ответ

Check if all elements in a list are identical

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

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

Я чувствую, что было бы лучше выполнить итерацию по списку, сравнивая соседние элементы, а затем AND все результирующие логические значения. Но я не уверен, какой наиболее питонический способ сделать это.

Переведено автоматически
Ответ 1

Использование itertools.groupby (см. the itertools Рецепты):

from itertools import groupby

def all_equal(iterable):
g = groupby(iterable)
return next(g, True) and not next(g, False)

или без groupby:

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

Существует ряд альтернативных однострочников, которые вы могли бы рассмотреть:


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


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


  2. Сравнение с входным списком без первого элемента


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


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


    def all_equal_ivo(lst):
    return not lst or lst.count(lst[0]) == len(lst)


  4. Сравнение со списком первого элемента повторяется


    def all_equal_6502(lst):
    return not lst or [lst[0]]*len(lst) == lst


Но у них есть некоторые недостатки, а именно:


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

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

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

  4. all_equal2 (в худшем случае) и all_equal_6502 создайте копию списка, что означает, что вам нужно использовать вдвое больше памяти.

В Python 3.9, используя perfplot, мы получаем эти тайминги (чем меньше Runtime [s], тем лучше):

для списка с разницей в первых двух элементах groupby работает быстрее всегодля списка без различий count(l[0]) выполняется быстрее всего

Ответ 2

Решение, более быстрое, чем использование 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
Ответ 3

[редактировать: этот ответ касается текущего ответа, набравшего наибольшее количество голосов itertools.groupby (что является хорошим ответом), позже.]

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

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

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

Это приведет к сбою в кратчайшие возможные сроки, поэтому это асимптотически оптимально (ожидаемое время составляет приблизительно O (# uniques), а не O (N), но в худшем случае время все равно O (N)). Предполагается, что вы раньше не видели эти данные...

(Если вы заботитесь о производительности, но не настолько сильно о производительности, вы можете просто сначала выполнить обычные стандартные оптимизации, такие как удаление константы myList[0] из цикла и добавление неуклюжей логики для крайнего случая, хотя это то, что компилятор python может в конечном итоге научиться делать, и поэтому не следует этого делать без крайней необходимости, поскольку это ухудшает читаемость при минимальном выигрыше.)

Если вы немного больше заботитесь о производительности, это в два раза быстрее, чем указано выше, но немного более подробно:

def allEqual(iterable):
iterator = iter(iterable)

try:
firstItem = next(iterator)
except StopIteration:
return True

for x in iterator:
if x!=firstItem:
return False
return True

Если вы еще больше заботитесь о производительности (но не настолько, чтобы переписывать свою программу), используйте набравший наибольшее количество голосов itertools.groupby ответ, который в два раза быстрее, чем allEqual потому что он, вероятно, оптимизирован для кода C. (Согласно документам, это не должно (аналогично этому ответу) иметь каких-либо накладных расходов на память, потому что отложенный генератор никогда не преобразуется в список ... о чем можно беспокоиться, но псевдокод показывает, что сгруппированные "списки" на самом деле являются отложенными генераторами.)

Если вас еще больше волнует производительность, читайте дальше...


примечания относительно производительности, потому что в других ответах говорится об этом по какой-то неизвестной причине:

... если вы видели данные раньше и, вероятно, используете какую-то структуру данных коллекции, и вас действительно волнует производительность, вы можете получить .isAllEqual() бесплатно O(1), дополнив свою структуру Counter, Которая обновляется при каждой операции вставки / удаления / и т.д. И просто проверяя, имеет ли она вид {something:someCount} т.Е. len(counter.keys())==1; в качестве альтернативы вы можете сохранить счетчик сбоку в отдельной переменной. Это доказуемо лучше, чем что-либо еще с точностью до constant factor. Возможно, вы также можете использовать FFI в python с ctypes выбранным вами методом и, возможно, с эвристикой (например, если это последовательность с getitem , затем проверяйте первый элемент, последний элемент, затем элементы по порядку).

Конечно, есть кое-что, что нужно сказать для удобства чтения.

Ответ 4

Преобразуйте ваши входные данные в set:

len(set(the_list)) <= 1

Использование set удаляет все повторяющиеся элементы. <= 1 это значит, что он правильно возвращает, True когда входные данные пусты.

Для этого требуется, чтобы все элементы во входных данных были хэшируемыми. Вы получитеTypeError, например, если передадите список списков.

2023-08-04 07:47 python algorithm