Отримання індексу повернутого елемента max або min, використовуючи max () / min () у списку


465

Я використовую Python maxі minфункції у списках для алгоритму minimax, і мені потрібен індекс значення, повернутий max()або min(). Іншими словами, мені потрібно знати, який хід призвів до максимального (на черзі першого гравця) або мінімального (другого гравця).

for i in range(9):
    newBoard = currentBoard.newBoardWithMove([i / 3, i % 3], player)

    if newBoard:
        temp = minMax(newBoard, depth + 1, not isMinLevel)  
        values.append(temp)

if isMinLevel:
    return min(values)
else:
    return max(values)

Мені потрібно мати можливість повернути фактичний індекс значення min або max, а не лише значення.


32
Вбудований divmodіснує, щоб запобігти тому, щоб сказати [i / 3, i % 3]багато.
Майк Грехем

Відповіді:


416
якщо єMinLevel:
    повернути значення.index (хв (значення))
ще:
    повернути значення.index (max (значення))

38
@KevinGriffin, зауважте, що це дає вам лише одне із можливих кількох випадків мінімального / максимального. Це може бути не те, що ви хочете, наприклад, якщо можливо збільшити свій прибуток тими ж двома способами, але один з них більше шкодить іншому гравцеві. Я не знаю, чи це справа, яку потрібно розглянути.
Майк Грехем

89
@Kashyap Це насправді O (N), а не O (N ^ 2). У випадку min оцінюється спочатку min (значень), що є O (N), потім називається value.index (), що також є O (N). O (N) + O (N) = O (N). Аргумент для індексу оцінюється лише один раз. Це еквівалентно:tmp = min(values); return values.index(tmp)
Том Карзес

@too much php що робити, коли відбувається повторення елементів.?
Шаші Тунга

@ShashiTunga [список] .index () повертає лише перше виникнення чогось, не гарантується, що воно є ексклюзивним, мінімальне значення може бути не унікальним у списку
Скотт Андерсон

471

Скажіть, що у вас є список values = [3,6,1,5], і вам потрібен індекс найменшого елемента, тобто index_min = 2в цьому випадку.

Уникайте рішення, itemgetter()представленого в інших відповідях, і використовуйте замість цього

index_min = min(range(len(values)), key=values.__getitem__)

тому що це не вимагає і import operatorне використовувати enumerate, і це завжди швидше (орієнтир нижче), ніж рішення, що використовує itemgetter().

Якщо ви маєте справу з рядовими масивами або можете дозволити собі це numpyяк залежність, розгляньте також використання

import numpy as np
index_min = np.argmin(values)

Це буде швидше, ніж перше рішення, навіть якщо ви застосовуєте його до чистого списку Python, якщо:

  • він більший, ніж кілька елементів (приблизно 2 ** 4 елемента на моїй машині)
  • ви можете дозволити собі копію пам'яті з чистого списку до numpyмасиву

як вказує цей орієнтир: введіть тут опис зображення

Я встановив орієнтир на своїй машині з python 2.7 для двох вищезазначених рішень (синій: чистий пітон, перший розчин) (червоний, нумерований розчин) та для стандартного рішення на основі itemgetter()(чорний, еталонний розчин). Той самий тест з python 3.5 показав, що методи порівнюють точно той самий випадок python 2.7, представлений вище


Дуже сильний +1. Я люблю тестування запропонованих рішень та правил, які ви узагальнили. Як я запропонував в іншій відповіді нижче, чи можете ви надати (або посилання на) свій тестовий код, щоб інші могли відтворити ваші результати? Машини та бібліотеки змінюються з часом, і це дозволить порівнювати інші рішення.
Ракурай

3
Я думаю, може бути помилка друку: xrange. Чи не повинен бути діапазон?
Ліндсі Фоулер

6
@LindsayFowler xrange()тепер застарілий, ви можете використовуватиrange()
davide

np.argmin не працює для плавців. лише перша пропозиція працює на ints та floats.
jimh

Думаю, ти помилився, спробуй import numpy as np; x = [2.3, -1.4]; np.argmin(x). Ви побачите, що argminпрацює і на поплавках
gg349

332

Ви можете одночасно знайти індекс та значення min / max, якщо перераховувати елементи у списку, але виконувати min / max за вихідними значеннями списку. Так:

import operator
min_index, min_value = min(enumerate(values), key=operator.itemgetter(1))
max_index, max_value = max(enumerate(values), key=operator.itemgetter(1))

Таким чином список буде проходити лише один раз протягом min (або max).


110
Або використовувати лямбда:key=lambda p: p[1]
Scry

116

Якщо ви хочете знайти індекс max у списку чисел (що здається вашим випадком), то я пропоную вам використовувати numpy:

import numpy as np
ind = np.argmax(mylist)

У разі багаторазового виникнення максимальних значень повертаються індекси, що відповідають першому виникненню.
Коензій

41

Можливо, більш простим рішенням було б перетворити масив значень у масив значень, пар індексів та взяти макс / хв цього. Це дало б найбільший / найменший показник, який має max / min (тобто пари порівнюються, спочатку порівнюючи перший елемент, а потім порівнюючи другий елемент, якщо перший є однаковим). Зауважте, що фактично створювати масив не потрібно, оскільки min / max дозволяють генераторам вводити дані.

values = [3,4,5]
(m,i) = max((v,i) for i,v in enumerate(values))
print (m,i) #(5, 2)

30
list=[1.1412, 4.3453, 5.8709, 0.1314]
list.index(min(list))

Дасть вам перший мінімум індексу.


18

Я думаю, що найкраще зробити, це перетворити список на a numpy arrayі використовувати цю функцію:

a = np.array(list)
idx = np.argmax(a)

14

Я також зацікавився цим і порівняв деякі запропоновані рішення за допомогою perfplot (мій проект для домашніх тварин).

Виявляється аргумент numpy ,

numpy.argmin(x)

є найшвидшим методом для достатньо великих списків, навіть при неявному перетворенні з вхідного listв а numpy.array.

введіть тут опис зображення


Код для створення сюжету:

import numpy
import operator
import perfplot


def min_enumerate(a):
    return min(enumerate(a), key=lambda x: x[1])[0]


def min_enumerate_itemgetter(a):
    min_index, min_value = min(enumerate(a), key=operator.itemgetter(1))
    return min_index


def getitem(a):
    return min(range(len(a)), key=a.__getitem__)


def np_argmin(a):
    return numpy.argmin(a)


perfplot.show(
    setup=lambda n: numpy.random.rand(n).tolist(),
    kernels=[
        min_enumerate,
        min_enumerate_itemgetter,
        getitem,
        np_argmin,
        ],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    )

Зауважте, що той самий висновок розміщено вже вище в моїй відповіді, більше 2 років тому, з більшою інформацією про те, коли і чому можна використовувати армін або ні. Подумайте про видалення відповіді, що також не дає заслуги тому, що вже запропоновано на цій самій сторінці. Подумайте також про те, щоб переглянути інші відповіді на ТА щодо подібної поведінки: ви, схоже, не цитуєте фактичну відповідь, що забезпечує найкраще рішення у ваших аналізах ефективності. Це досить погано, особливо для тих, хто має реплікацію> 10K, яка була достатньо довго, щоб краще знати.
gg349

@ gg349, дуже хороші моменти, але він дає вихідний код для отримання результатів, роблячи це легко відтворюваним та пристосованим для порівняння інших рішень. Я погоджуюся, що він може розглянути можливість видалення цієї відповіді як дубліката, але, можливо, ви могли б додати значення своїй відповіді, включивши чи посилаючись на використаний вами код?
Ракурай


8

Після отримання максимальних значень спробуйте:

max_val = max(list)
index_max = list.index(max_val)

Набагато простіше, ніж безліч варіантів.


6

Я думаю, що відповідь вище вирішує вашу проблему, але я подумав, що я поділюсь методом, який дає вам мінімум, і всі показники, в яких є мінімум.

minval = min(mylist)
ind = [i for i, v in enumerate(mylist) if v == minval]

Це проходить список двічі, але все ще досить швидко. Однак це дещо повільніше, ніж пошук індексу першої зустрічі мінімуму. Тож якщо вам потрібен лише один із мінімумів, використовуйте рішення Метта Андерсона , якщо вони вам потрібні, використовуйте це.


1
Мені це подобається, тому що він використовує базовий Python, і мені здається, що розуміння списку легше зрозуміти, ніж itemgetter, лямбда тощо. (І досить гнучка для вирішення різноманітних завдань, таких як ця ....)
Джеймс,

сирий. Я вважаю за краще це.
Dev_Man

6

Використовуйте функцію модуля numpy numpy.where

import numpy as n
x = n.array((3,3,4,7,4,56,65,1))

Для індексу мінімального значення:

idx = n.where(x==x.min())[0]

Для індексу максимального значення:

idx = n.where(x==x.max())[0]

Насправді ця функція набагато потужніша. Ви можете створювати всі види булевих операцій Для значення індексу від 3 до 60:

idx = n.where((x>3)&(x<60))[0]
idx
array([2, 3, 4, 5])
x[idx]
array([ 4,  7,  4, 56])

індекс в python починається з 0. індекс повертається дорівнює 6 (для 65), тоді як ваш код повертає 7 (питання ОП було "Отримання індексу ...")
тагома

У команді я запитав на індекс мінімального значення (тут: 1), індекс IS 7. 65 - максимальне значення елементів масиву. Якщо ви введете: n.where (x == x.max ()) [0], ви отримаєте індекс макс. значення, яке тут становить 65. Його індекс вийде 6
Ішан Томар

використання numpy: можливо, заборонено в цій програмі. Але якщо ви збираєтеся використовувати numpy, вам набагато краще просто використовувати argmin()замість того, що ви робили тут.
RBF06

Дякую @ RBF06, я перевірю це.
Ішан Томар

5

Це просто можливо за допомогою вбудованої enumerate()та max()функції та необов'язкового keyаргументу max()функції та простого лямбда-виразу:

theList = [1, 5, 10]
maxIndex, maxValue = max(enumerate(theList), key=lambda v: v[1])
# => (2, 10)

У документах для max()нього сказано, що keyаргумент очікує функції, як у list.sort()функції. Також дивіться сортування як це робити .

Це працює так само і для min(). Btw повертає перше значення max / min.


Пізня, але найкраща відповідь (якщо у вас немає потреби в швидкості).
mmj

5

Скажіть, у вас є такий список, як:

a = [9,8,7]

Наступні два способи є досить компактними способами отримання кортежу з мінімальним елементом та його індексом. Обидва беруть схоже час для обробки. Мені краще подобається метод zip, але це мій смак.

zip метод

element, index = min(list(zip(a, range(len(a)))))

min(list(zip(a, range(len(a)))))
(7, 2)

timeit min(list(zip(a, range(len(a)))))
1.36 µs ± 107 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

перерахувати метод

index, element = min(list(enumerate(a)), key=lambda x:x[1])

min(list(enumerate(a)), key=lambda x:x[1])
(2, 7)

timeit min(list(enumerate(a)), key=lambda x:x[1])
1.45 µs ± 78.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

4

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

max_index = max( range( len(my_list) ), key = lambda index : my_list[ index ] )

Дуже чисто! І на відміну від прийнятої відповіді, це правда O (n), правда? Я знаю, що O (2n) вважається O (n), але для дуже великих nвін може бути помітно повільніше.
kevlarr


3

Навіщо турбуватися спочатку додавати індекси, а потім перевертати їх? Функція перерахування () - це лише особливий випадок використання функції zip (). Давайте скористаємося відповідним чином:

my_indexed_list = zip(my_list, range(len(my_list)))

min_value, min_index = min(my_indexed_list)
max_value, max_index = max(my_indexed_list)

2

Просто незначне доповнення до вже сказаного. values.index(min(values))здається, повертає найменший показник хв. Наступний отримує найбільший індекс:

    values.reverse()
    (values.index(min(values)) + len(values) - 1) % len(values)
    values.reverse()

Останній рядок може бути залишений, якщо побічний ефект повернення на місці не має значення.

Повторити всі події

    indices = []
    i = -1
    for _ in range(values.count(min(values))):
      i = values[i + 1:].index(min(values)) + i + 1
      indices.append(i)

Заради стислості. Напевно, краща ідея кешувати min(values), values.count(min)поза циклом.


2
reversed(…)замість цього ….reverse(), швидше за все, бажано, оскільки він не мутує і повертає генератор в будь-якому випадку. І всі події також можуть бутиminv = min(values); indices = [i for i, v in enumerate(values) if v == minv]
HoverHell

2

Простий спосіб пошуку індексів з мінімальним значенням у списку, якщо ви не хочете імпортувати додаткові модулі:

min_value = min(values)
indexes_with_min_value = [i for i in range(0,len(values)) if values[i] == min_value]

Потім виберіть для прикладу перший:

choosen = indexes_with_min_value[0]

1

У вас немає достатньо високого представника, щоб прокоментувати існуючу відповідь.

Але для https://stackoverflow.com/a/11825864/3920439 відповідь

Це працює для цілих чисел, але не працює для масиву плавців (принаймні, у python 3.6) TypeError: list indices must be integers or slices, not float


0

https://docs.python.org/3/library/functions.html#max

Якщо декілька елементів є максимальними, функція повертає перше, що виникло. Це узгоджується з іншими інструментами збереження стабільності сортування, такими якsorted(iterable, key=keyfunc, reverse=True)[0]

Щоб отримати більше, ніж просто перший, використовуйте метод сортування.

import operator

x = [2, 5, 7, 4, 8, 2, 6, 1, 7, 1, 8, 3, 4, 9, 3, 6, 5, 0, 9, 0]

min = False
max = True

min_val_index = sorted( list(zip(x, range(len(x)))), key = operator.itemgetter(0), reverse = min )

max_val_index = sorted( list(zip(x, range(len(x)))), key = operator.itemgetter(0), reverse = max )


min_val_index[0]
>(0, 17)

max_val_index[0]
>(9, 13)

import ittertools

max_val = max_val_index[0][0]

maxes = [n for n in itertools.takewhile(lambda x: x[0] == max_val, max_val_index)]

0

Як що до цього:

a=[1,55,2,36,35,34,98,0]
max_index=dict(zip(a,range(len(a))))[max(a)]

Він створює словник із елементів у aвигляді ключів та їх індексів як значень, таким чином dict(zip(a,range(len(a))))[max(a)]повертає значення, яке відповідає ключу, max(a)який є індексом максимуму в Я новачок у python, тому не знаю про складність цього рішення в обчислювальній техніці.

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