简体   繁体   中英

Find index of minimum value in a Python sublist - min() returns index of minimum value in list

I've been working on implementing common sorting algorithms into Python, and whilst working on selection sort I ran into a problem finding the minimum value of a sublist and swapping it with the first value of the sublist, which from my testing appears to be due to a problem with how I am using min() in my program.

Here is my code:

def selection_sort(li):
    for i in range(0, len(li)):
        a, b = i, li.index(min(li[i:]))
        li[a], li[b] = li[b], li[a]

This works fine for lists that have zero duplicate elements within them:

>>> selection_sort([9,8,7,6,5,4,3,2,1])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

However, it completely fails when there are duplicate elements within the list.

>>> selection_sort([9,8,8,7,6,6,5,5,5,4,2,1,1])
[8, 8, 7, 6, 6, 5, 5, 5, 4, 2, 9, 1, 1]

I tried to solve this problem by examining what min() is doing on line 3 of my code, and found that min() returns the index value of the smallest element inside the sublist as intended, but the index is of the element within the larger list rather than of the sublist, which I hope this experimentation helps to illustrate more clearly:

>>> a = [1,2,1,1,2]
>>> min(a)
1                       # expected
>>> a.index(min(a))
0                       # also expected
>>> a.index(min(a[1:]))
0                       # should be 1?

I'm not sure what is causing this behaviour; it could be possible to copy li[i:] into a temporary variable b and then do b.index(min(b)) , but copying li[i:] into b for each loop might require a lot of memory, and selection sort is an in-place algorithm so I am uncertain as to whether this approach is ideal.

You're not quite getting the concept correctly!

li.index(item) will return the first appearance of that item in the list li. What you should do instead is if you're finding the minimum element in the sublist, search for that element in the sublist as well instead of searching it in the whole list. Also when searching in the sliced list, you will get the index in respect to the sublist. Though you can easily fix that by adding the starting step to the index returned.

A small fix for your problem would be:

def selection_sort(li):
    for i in range(0, len(li)):
        a, b = i, i + li[i:].index(min(li[i:]))
        li[a], li[b] = li[b], li[a]

One way you can do it is using list comprehension:

idxs = [i for i, val in enumerate(a) if val == min(a)]

Or even better, write your own code, which is faster asymptotically:

idxs = []
minval = None
for i, val in enumerate(a):
    if minval is None or minval > val:
        idxs = [i]
        minval = val
    elif minval == val:
        idxs.append(i)

When you write a.index(min(a[1:])) you are searching for the first occurence of the min of a[1:] , but you are searching in the original list . That's why you get 0 as a result.

By the way, the function you are looking for is generally called argmin . It is not contained in pure python, but numpy module has it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM