Получение индекса возвращенного элемента max или min с использованием max () / min () в списке


467

Я использую Python maxи minфункции в списках для минимаксного алгоритма, и мне нужен индекс значения, возвращаемого 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)

Мне нужно иметь возможность возвращать фактический индекс минимального или максимального значения, а не только значение.


32
Встроенный divmodсуществует, чтобы не сказать [i / 3, i % 3]много.
Майк Грэм

Ответы:


417
если isMinLevel:
    возвращаемые значения. индекс (мин (значения))
еще:
    return values.index (max (values))

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

89
@Kashyap На самом деле это O (N), а не O (N ^ 2). В случае min сначала оценивается min (значения), который равен O (N), затем вызывается values.index (), который также равен O (N). O (N) + O (N) = O (N). Аргумент для индексации оценивается только один раз. Это эквивалентно:tmp = min(values); return values.index(tmp)
Тому Карзесу

@ слишком много PHP, что делать, когда есть повторение элементов.?
Шаши Тунга

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

473

Скажите, что у вас есть список values = [3,6,1,5], и вам нужен индекс наименьшего элемента, т.е. index_min = 2в этом случае.

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

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

потому что он не требует import operatorни использовать enumerate, и он всегда быстрее (тест ниже), чем решение, использующее itemgetter().

Если вы имеете дело с массивами NumPy или можете позволить себе 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 не работает для поплавков. только первое предложение работает на бронях и поплавках.
jimh

Я думаю, что вы ошибаетесь, попробуйте import numpy as np; x = [2.3, -1.4]; np.argmin(x). Вы увидите, что это argminработает и на поплавках
gg349

332

Вы можете найти мин / макс индекс и значение одновременно, если вы перечисляете элементы в списке, но выполняете мин / макс для исходных значений списка. Вот так:

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

Таким образом, список будет проходиться только один раз за мин (или макс).


110
Или используйте лямбду:key=lambda p: p[1]
scry

116

Если вы хотите найти индекс max в списке чисел (что кажется вашим случаем), то я предлагаю вам использовать numpy:

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

В случае множественных вхождений максимальных значений, возвращаются индексы, соответствующие первому вхождению.
Cohensius

41

Возможно, более простое решение состояло бы в том, чтобы превратить массив значений в массив значений, индексные пары и взять максимум / мин этого. Это дало бы наибольший / наименьший индекс, который имеет максимум / мин (т.е. пары сравниваются, сначала сравнивая первый элемент, а затем сравнивая второй элемент, если первые совпадают). Обратите внимание, что на самом деле не нужно создавать массив, потому что 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 лет назад, с дополнительной информацией о том, когда и почему можно использовать argmin или нет. Подумайте об удалении ответа, который также не дает оснований для того, что уже было предложено на этой же странице. Также рассмотрите другие ваши ответы на SO для аналогичного поведения: вы, кажется, не цитируете фактический ответ, предоставляющий лучшее решение в ваших анализах производительности. Это довольно плохо, особенно для кого-то с> 10К повторений, который был достаточно длинным, чтобы знать лучше.
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, lambda и т. Д. (И достаточно гибкое, чтобы решать различные задачи, такие как этот ...)
Джеймс

в сыром виде . Я предпочитаю это.
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])

index в python начинается с 0. Возвращаемое значение index должно быть 6 (для 65), а ваш код возвращает 7 (вопрос OP был «Получение индекса ...»)
tagoma

В этой команде я запросил индекс минимального значения (здесь: 1), индекс которого равен 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(). Кстати, он возвращает первое максимальное / минимальное значение.


Поздний, но лучший ответ (если вам не нужна скорость).
MMJ

5

Скажем, у вас есть список, такой как:

a = [9,8,7]

Следующие два метода - довольно компактные способы получить кортеж с минимальным элементом и его индексом. Оба требуют одинакового времени для обработки. Мне больше нравится метод 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он может быть заметно медленнее.
Кевлар


3

Зачем сначала добавлять индексы, а потом наоборот? Функция enumerate () - это особый случай использования функции 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)который является индексом максимума в a. Я новичок в Python, поэтому я не знаю о вычислительной сложности этого решения.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.