Пошук першого та останнього індексу деякого значення у списку на Python


81

Чи є якісь вбудовані методи, які є частиною списків, які давали б мені перший і останній індекс якогось значення, наприклад:

verts.IndexOf(12.345)
verts.LastIndexOf(12.345)

2
Я їх теж читаю, але нічого не знаючи про python ускладнює ситуацію. Я вважаю його синтаксис дуже загадковим.
Джоан Венге

Якщо ваші списки відсортовані, ви можете перевірити модуль бісект < docs.python.org/3/library/bisect.html >.
tommy.carstensen

Дивіться блискуча відповідь на Dikei тут , використовуючи вбудовані функції перерахування і звернений: stackoverflow.com/questions/9836425 / ...
tommy.carstensen

Відповіді:


124

Послідовності мають метод, index(value)який повертає індекс першого входження - у вашому випадку це буде verts.index(value).

Ви можете запустити його, verts[::-1]щоб дізнатися останній індекс. Ось, це було бlen(verts) - 1 - verts[::-1].index(value)


Дякую, як виконується список [:: - 1]? verts [:: - 1]?
Джоан Венге

3
Btw verts [:: - 1] просто змінює список, чи не так? Тож я повинен компенсувати індекс, так?
Джоан Венге

13
звичайно, це буде: len (verts) - 1 - verts [:: - 1] .index (value)
SilentGhost


11

Як невелика допоміжна функція:

def rindex(mylist, myvalue):
    return len(mylist) - mylist[::-1].index(myvalue) - 1

9

Мабуть, два найефективніші способи знайти останній індекс:

def rindex(lst, value):
    lst.reverse()
    i = lst.index(value)
    lst.reverse()
    return len(lst) - i - 1
def rindex(lst, value):
    return len(lst) - operator.indexOf(reversed(lst), value) - 1

Обидва вони займають лише додатковий простір O (1), і два розвороти на місці першого рішення набагато швидші, ніж створення зворотної копії. Давайте порівняємо це з іншими рішеннями, опублікованими раніше:

def rindex(lst, value):
    return len(lst) - lst[::-1].index(value) - 1

def rindex(lst, value):
    return len(lst) - next(i for i, val in enumerate(reversed(lst)) if val == value) - 1

Тестові результати, мої рішення - це червоний та зелений: неперетасований, повний діапазон

Це для пошуку числа у списку з мільйона чисел. Вісь х - для розташування шуканого елемента: 0% означає, що він знаходиться на початку списку, 100% означає, що він знаходиться в кінці списку. Усі рішення найшвидші за місцем розташування на 100%, причому два reversedрішення майже не забирають на це часу, рішення з подвійним зворотом займає трохи часу, а зворотне копіювання займає багато часу.

Детальніший погляд на правий кінець: неперетасована, хвостова частина

У місці 100% рішення для зворотної копії та рішення для подвійного звороту витрачають весь свій час на розвороти ( index()миттєво), тому ми бачимо, що два розвороти на місці приблизно в сім разів швидші, ніж створення зворотної копії.

Вище було з lst = list(range(1_000_000, 2_000_001)), що в значній мірі створює об'єкти int послідовно в пам'яті, що надзвичайно зручно для кешу. Давайте зробимо це ще раз, перетасувавши список random.shuffle(lst)(можливо, менш реалістичним, але цікавим):

перемішаний список, повний асортимент

перемішаний список, хвостова частина

Як і очікувалося, все стало набагато повільніше. Рішення зворотної копії страждає найбільше, на 100% це займає зараз приблизно в 32 рази (!) Стільки часу, скільки рішення подвійної зворотної копії. І enumerate-рішення зараз є другим за швидкістю лише після розташування 98%.

Загалом, мені найбільше подобається operator.indexOfрішення, оскільки воно є найшвидшим за останню половину або чверть усіх місць, які, мабуть, є найцікавішими, якщо ви насправді rindexщось робите . І це лише трохи повільніше, ніж рішення з подвійним зворотом у попередніх місцях.

Усі тести виконані з CPython 3.9.0 64-bit 64-розрядної версії Windows 10 Pro 1903.


5

Списки Python мають index()метод, за допомогою якого ви можете знайти позицію першого входження елемента у список. Зверніть увагу, що list.index()підвищується, ValueErrorколи елемента немає у списку, тому вам може знадобитися обернути його try/ / except:

try:
    idx = lst.index(value)
except ValueError:
    idx = None

Щоб ефективно знайти позицію останнього входження елемента в список (тобто без створення зворотного проміжного списку), ви можете використовувати цю функцію:

def rindex(lst, value):
    for i, v in enumerate(reversed(lst)):
        if v == value:
            return len(lst) - i - 1  # return the index in the original list
    return None    

print(rindex([1, 2, 3], 3))     # 2
print(rindex([3, 2, 1, 3], 3))  # 3
print(rindex([3, 2, 1, 3], 4))  # None

0

Цей метод можна оптимізувати, ніж вище

def rindex(iterable, value):
    try:
        return len(iterable) - next(i for i, val in enumerate(reversed(iterable)) if val == value) - 1
    except StopIteration:
        raise ValueError

4
"Оптимізовано" в сенсі ефективнішого використання простору, але це приблизно на 50% повільніше.
dubiousjim

0
s.index(x[, i[, j]])

індекс першого входження x у s (при або після індексу i та перед індексом j)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.