Selection Sort Python - python

This may seem like a simple question but when I attempted to implement selection sort in Python, I do not get a sorted list. Is there something wrong with my implementation? The subsetting may be a problem.
source = [4,2,1,10,5,3,100]
for i in range(len(source)):
mini = min(source[i:]) #find minimum element
min_index = source[i:].index(mini)-1 #find index of minimum element
source[i:][min_index]= source[i:][0] #replace element at min_index with first element
source[i:][0] = mini #replace first element with min element
print source

I think there were a couple issues.
First, when your do source[i:], I believe that returns a new array of the sub-elements requested and not part of the original array, thus if you modify it, your don't modify the original. Second, you were subtracting 1 from an index when you shouldn't.
source = [4,2,1,10,5,3,100]
for i in range(len(source)):
mini = min(source[i:]) #find minimum element
min_index = source[i:].index(mini) #find index of minimum element
source[i + min_index] = source[i] #replace element at min_index with first element
source[i] = mini #replace first element with min element
print source
This gives:
[1, 2, 3, 4, 5, 10, 100]

Here is how I would rewrite your code. Of course in Python I would just use list.sort() to sort a list, but here is a selection sort in Python.
We make a generator expression that returns tuples of (value, i) for a value and its index from the list. Then when min() evaluates to find minimum, it finds the lowest tuple value; since the value comes first in the tuple before the index, the value will be the important part, and min() will find the lowest value. (If there is a tie, min() will use the second part of the tuple, the index, as a tie-breaker. But for sort we don't care how ties are broken.)
Now, instead of searching through the sub-list to find the min value, and then searching through it again to figure out the index, we search through it once and get both min value and index.
But we don't actually care about the min value; we care about the index. So after min() is done, we just throw away the actual value but keep the index. Adjust the index to be correct in the whole list (not in the slice of the list) and then we can swap.
We use the standard Python idiom for swapping two values. Python will build a tuple object to be the intermediate, then unpack this tuple into the left-hand-side.
lst = [4,2,1,10,5,3,100]
for i_sortpos in range(len(lst)):
# Make a generator expression to return (value, i) pairs.
genexp = ((n, i) for i, n in enumerate(lst[i_sortpos:]))
# Use genexp with min() to find lowest and its index.
# (Use '_' for variable name for the actual value; we don't use it.)
_, i_min = min(genexp)
# Adjust index to be correct in full list.
i_min += i_sortpos
# Swap the number at i_sortpos with the lowest found.
lst[i_sortpos], lst[i_min] = lst[i_min], lst[i_sortpos]
print(lst)
EDIT: And here is a refinement of the above. A slice from a list actually allocates a new list; our code here doesn't need a new list, it just needs a convenient way to examine a sublist. The itertools module offers a function, islice(), that returns an iterator that iterates over a slice of a list. This avoids repeatedly creating and destroying lists as we examine each sublist.
I believe this is the most efficient way to do selection sort in Python. (You could get rid of the part where we bind the generator expression to the name genexp and save a few microseconds... just make the call to min() a long one-liner. But it's not really worth the loss of readability.)
import itertools as it
lst = [4,2,1,10,5,3,100]
for i_sortpos in range(len(lst)):
# Make a generator expression to return (value, i) pairs.
# Use it.islice() for to look at sublist.
genexp = ((n, i) for i, n in enumerate(it.islice(lst, i_sortpos, len(lst))))
# Use genexp with min() to find lowest and its index.
# (Use '_' for variable name for the actual value; we don't use it.)
_, i_min = min(genexp)
# Adjust index to be correct in full list.
i_min += i_sortpos
# Swap the number at i_sortpos with the lowest found.
lst[i_sortpos], lst[i_min] = lst[i_min], lst[i_sortpos]
print(lst)

def selectionSort(List_):
for i in range(len(List_)):`
#track the current smallest value
smallIndex = i
#loop from the current smallest value
for j in range(i+1,len(List_))
if List_[j] < List_[smallIndex]:
#if new value is less that our smallest value,change
#smallest value to this
smallIndex = j
if smallIndex != i:
#swap the values
List_[smallIndex],List_[i] = List_[i],List_[smallIndex]
#return sorted list
return List_

def ss(l):
for i in range(0,len(l)):
d=l.index(min(l[i:]))
c=l[i]
l[i]=min(l[i:])
l[d]=c
print(l) #it prints each step of selection sort
y=[10,9,1,5,0,6]
ss(y)

def selSort(L):
"""
Find the smallest element in the list and put it (swap it) in the first location,
Find the second element and put it (swap it) in the second locaiton, and so on.
"""
for i in range(len(L) - 1):
minIndx = i
minVal= L[i]
j = i + 1
while j < len(L):
if minVal > L[j]:
minIndx = j
minVal= L[j]
j += 1
temp = L[i]
L[i] = L[minIndx]
L[minIndx] = temp
return L
Call:
print( selSort([120,11,0,1,3,2,3,4,5,6,7,8,9,10]) )
Output
[0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 120]

s = [1,8,4,9,3,6,2]
for i in range(len(s)):
maxi = max(s[0:len(s)-i]) #find max element
tempi = s.index(maxi) # find index of max element
temp = s[len(s)-1-i] #assign last element as temp
s[len(s)-1-i] = maxi #put max element in last position
s[tempi] = temp # put the element initially at last in its new
print s

Find the position(first and last), swap the elements if last is lower.
nums = [4,2,1,10,5,3,100]
def sort(nums):
###Find the position and now first 0th element is sorted and rest is unsorted
#Second iteration first 2 element is sorted
for i in range(len(nums)-1):
miniposition = i
for j in range(i,len(nums)):
if nums[j] < nums[miniposition]:
miniposition = j
temp = nums[i]
nums[i] = nums[miniposition]
nums[miniposition] = temp
sort(nums)
print (nums)
First iteration(swapped 4 and 1)
[1, 2, 4, 10, 5, 3, 100]
[1, 2, 4, 10, 5, 3, 100]
[1, 2, 3, 10, 5, 4, 100]
[1, 2, 3, 4, 5, 10, 100]
[1, 2, 3, 4, 5, 10, 100]
[1, 2, 3, 4, 5, 10, 100]
Other way
nums = [4,2,1,10,5,3,100]
i = 0
while i<len(nums):
#smallest element in the sublist
smallest = min(nums[i:])
#index of smallest element
index_of_smallest = nums.index(smallest)
#swapping
nums[i],nums[index_of_smallest] = nums[index_of_smallest],nums[i]
i=i+1
print (nums)

a slight variation of the solution provided
def selection_sort(l):
i = 0
while i < len(l):
minium_value = min(l[i:]) # minium value at ith iteration
minium_value_index = l[i:].index(minium_value) # minium value index at i th iteration
if minium_value < l[i]: # if the current value already min, skip
l[i + minium_value_index] = l[i] # put current value in min value's index - swap 1
l[i] = minium_value # set current value with min value- swap 2
i += 1
return l

def selection_sort_min(): # sorting number
for i in range(len(num)-1):
current_min_index = i
for j in range(i+1,len(num)):
if num[j] < num[current_min_index] :
current_min_index = j
num[i],num[current_min_index] = num [current_min_index],num[i]
print(num)
num = [23,89,12,0,3,7,33]
selection_sort_min()

here is what I think is a good way to sort a list of numbers and I hope it helps:
list=[5,4,3,1,6,8,10,9]
listsorted=[]
for i in range(len(list)):
x=min(list)
list.remove(x)
listsorted.append(x)
print listsorted
and the result will be [1, 3, 4, 5, 6, 8, 9, 10]

I think the "accepted" answer here is unhelpful. If we look at e.g.
mini = min(source[i:]) #find minimum element
min_index = source[i:].index(mini) #find index of minimum element
not only is this inefficient in terms of creating list slices unnecessarily, but they are searched unnecessarily. It's reasonably concise but I don't think it's the best solution.

def Selection_Sort(Sarray):
length = len(Sarray)
i = 0
j = 0
for i in range(length):
j = i+1
for j in range(length):
if Sarray[i] < Sarray[j]
t = Sarray[i]
Sarray[i] = Sarray[j]
Sarray[j] = t
j = j+1
i = i+1
return Sarray

Code of select sort from MIT online course .
def selSort(L):
for i in range(len(L) - 1):
minIndx = i
minVal = L[i]
j = i+1
while j < len(L):
if minVal > L[j]:
minIndx = j
minVal = L[j]
j += 1
if minIndx != i:
temp = L[i]
L[i] = L[minIndx]
L[minIndx] = temp

def selectSort(L):
for i in range(len(L)):
print L
minIndex = i
minValue = L[i]
j = i + 1
while j < len(L):
if minValue > L[j]:
minIndex = j
minValue = L[j]
j +=1
temp = L[i]
L[i] = L[minIndex]
L[minIndex] = temp

Related

finding index[] of odd values in a list of integers?

I am trying to find the index value of some odd integers in a list. The program at its current stage only returns a new list containing only the odd integers.
image of my code
This program returns the values [3, 5, 7]. How would i be able to retrieve the index of these odd values instead? For example, [0, 1, 3, 5] for the list shown above.
I have tried "position = dataItem1.index()". I understand that i would need to input an index value within the index brackets.
you can use also use enumerate : it returns the index of the element and the element (also, in python you can loop directly over the elements of a list, no need of while loops). I also encourage the use of .append() to add an element to a list, this is clearly more efficient than concatenate two lists as you do:
l = [0, 3, 2, 3, 4, 7, 6]
def get_odds_index(l):
res = []
for idx, val in enumerate(l):
if val % 2 != 0:
res.append(idx)
return res
get_odds_index(l)
Instead of adding the element on that index in the odd list, just add index
Like this
def positionOfOdds(arr):
odds = []
length = len(arr)
index = 0
while index < length:
data = arr[index]
if data % 2 != 0:
odds.append(index)
index += 1
return odds
you already know the index, it is the loop variable
def positionOfOdds(arr):
odds = []
length = len(arr)
index = 0
while index < length:
data = arr[index]
if data % 2 != 0:
odds = odds + [index]
index += 1
return odds
You use an array called odds to store the odd numbers, to get the indexes you can create instead an array to store the indexes of the odd numbers, which I called odds_idxs. It should look something like this:
num_list = [0, 3, 2, 3, 4, 7, 6]
def position_of_odds(num_list):
odds_idxs = []
length = len(num_list)
index = 0
while index < length:
if num_list[index] % 2 != 0:
odds_idxs = odds_idxs + [index]
index = index + 1
return odds_idxs

Problem while performing insertion sort while simultaneously adding values

I made a program to take elements from a list and a tuple simultaneously {ie. one from list then from tuple and then back from list and so on} and placing them in a new list and sorting them while inserting using insertion sort. The problem I am facing is the numbers are not sorting properly.
the output I'm getting [8, 6, 9, 4, 3, 1, 2, 5, 7]
the output I want. [1,2,3,4,5,6,7,8,9]
list = [8,6,4,2]
tuple = (9,5,7,1,3)
final = [list[0],] # creating a final list with the value list[0]
list.pop(0) # removing the first element from list
l = len(list)
t = len(tuple)
# now comparing whoes length is greater and assigning it to j
if l > t:
j = l
else:
j = t
b = 0 # variable to keep the count of elements in the final list starting from 0 to n-1
for i in range(j):
n = b
if i < l: # Making sure the index dosen't exceed the list
temp = list[i]
final.append(temp) # appending the value to last
while temp < final[n] & n>-1:
final[n+1] = final[n]
n = n-1
final[n+1] = temp
b+=1 # a value is added to final list so the total no of elements increases
n=b
if i < t: # Making sure the index dosen't exceed the tuple
temp = tuple[i]
final.append(temp)
while temp < final[n] & n>-1:
final[n+1] = final[n]
n = n-1
final[n+1] = temp
b+=1
print(final)
You can just use the .sort() method to get the correct order.
...
b+=1
final.sort()
print(final)

Random walk Python exercise loop

I am doing python exercise and I can not finish it. I need to a create array and fill it 25 numbers, every number will be the sum of previous one and the value drawn from the set (-5, 5).
import numpy as np
import random
def prog_list(mylist):
mylist = [0]
addlist = [-5,5]
sum_list = 0
for i in mylist:
if len(mylist) < 25:
sum_list = random.choice(addlist) + mylist[i-1]
mylist.append(sum_list)
else:
return mylist
for x in prog_list(mylist):
print(x)
When I print x I've got
IndexError: list index out of range
this works:
def prog_list(n):
mylist = [0]
#addlist = [-5,5]
addlist = list(range(-5, 5+1))
for i in range(1, n):
_sum = random.choice(addlist) + mylist[i-1]
mylist.append(_sum)
return mylist
prog_list(25)
if you want to draw any integer from (-5, 5) you need this:
addlist = list(range(-5, 5+1))
there is an even cleaner way to do it where you dont acces the list in every iteration:
def prog_list(n):
mylist = []
_sum = 0
#addlist = [-5,5]
addlist = list(range(-5, 5+1))
for i in range(n):
mylist.append(_sum)
_sum += random.choice(addlist)
return mylist
prog_list(25)
for i in mylist:
if len(mylist) < 100:
sum_list = random.choice(addlist) + mylist[i-1]
mylist.append(sum_list)
else:
return mylist
This construct will get value of element of mylist as i. First element is 0 so you get one of {-5,5} + mylist[-1] ([-1] means last element in python language), this result in either -5 or 5, then you get that value less 1 which is either -6 or 4, but there is not enough elements to get -6 (i.e. 6 from right) or 4 (i.e. 5 from left) element, thus IndexError. To avoid that you might replace your for using following while
while len(mylist) < 100:
sum_list = random.choice(addlist) + mylist[-1]
mylist.append(sum_list)
return mylist
for i in mylist
iterates over the items in your list, so on the first step when you add or subtract 5, you will be trying to access the (5 - 1)th or the (-5 - 1)th element with mylist[i - 1], and that will give you an IndexError.
To get the current last element of your mylist, you should use
mylist[-1] instead of mylist[i - 1]

Python: How to find the second highest number in a list?

def second_highest(list):
""" (list of int) -> int
How do you find the second highest value from the list of integers without using remove, pop, or sort (which I tried) since I need to use the same list later on?
There will be no duplication of numbers.
list.sort()
return list[-2]
I tried removing the highest number using max, sorting the list, but since those mutates the list, I can't use them.
Use the builtin sorted oy mylist, which will not modify mylist(thanks to #Tofystedeth)
mylist = [1, 2, 8, 3, 12]
print(sorted(mylist, reverse=True)[1])
data = [1,2,8,3,12]
largest = None
second_largest = None
for a in data:
if not largest or a > largest:
if largest:
second_largest = largest
largest = a
print("largest: {}".format(largest))
print("second_largest: {}".format(second_largest))
arr = [2, 3, 4, 2, 4, -3, 43, -4, -25, 45, 9]
my_list = list(set(arr))
my_list.sort()
if len(my_list) == 1:
print(my_list[0])
elif len(my_list) >= 2:
print(my_list[-2])
nums=[1,2,3,3,5,4,5,5,2]
#making new set to maintain uniqueness
new_list= set(nums)
high= max(nums)
new_list.remove(high)
print(max(new_list))
#This code uses remove but does not do any changes to the given list
print(nums)
##This will work even if numbers are negative. Logic is : convert the list() to set() and back to list to use the sort() method and then print the -2 position value in the list. This gives you the second-highest value.## "Python 3"
if __name__ == '__main__':
n = int(input())
arr = list(map(int, input().split()))
z = list(set(arr))
z.sort()
print(z[-2])
array = [2, 3, 4, 2, 4, -3, 43, -4, -25, 45, 9]
arr = array.copy()
z = max(arr)
# if list contains duplicate max elements removing them until We get Second highest
while max(arr) == z:
arr.remove(max(arr))
print(max(arr))
print(array)
Here is the code to find the 2nd largest number in the list without using any inbuilt functions.
data = [11,22,1,2,5,67,21,32]
max1 = data[0] # largest num
max2 = data[1] # second largest num
for num in data:
if num > max1:
max2 = max1 # Now this number would be second largest
max1 = num # This num is largest number in list now.
# Check with second largest
elif num > max2:
max2 = num # Now this would be second largest.
print(max2)
I think my answer is more simple and more readable for beginners
x=[2,3,4,2,5,9,8,4,5,6,7,8,9,2,1,3,4,5]
max=-10000000
for i in x:
if(i>max):
secondmax=max
max=i
elif(i>secondmax and i!=max):
secondmax=i
print(secondmax)
Here is a way to do it using sort. However, it gives you the possibility to reuse the list since you are temporarily storing the list arr in sortedArr. Calling arr by itself would return the original list. Here you can Try it online!
# Finding a runner up number in a List of Arrays
# Second highest number in a list
arr = [2,3,6,6,5]
sortedArr = sorted(arr,reverse=True) # Sorting the array in descending order.
highest = sortedArr[0] # Assign the highest value in the array to the variable `highest`.
secondHighest = 0 # Initializing the variable `secondHighest` to 0.
for x in (sortedArr): # Iterating through the sorted array and checking if the value is equal to the highest value.
if(x == highest):
continue # If it is, it will continue to the next value.
else:
secondHighest = x # If it is not, it will assign the value to the variable `secondHighest`
break # break out of the loop.
print(secondHighest) # Printing the value of the variable `secondHighest`.
>>> 5
list = [2,3,5,1,7,8]
secondhighest = max([i for i in list if i<max(a)])
This will give you the second highest value in the list.
Copy unique list elements to another list (if Already the list elements are unique, go to step 2) .
You should find the maximum in the list and save its index. Then remove it from the list using the remove() function and then find the maximum of the new list (with the original maximum value removed) and that will be your second highest element. You can then use the insert() method to add back the original maximum back into the list.
Here is the code to find the maximum and second maximum number in a list or array(python). Tried with all the combination of elements(negative and positive), it is working fine. We can optimize this code as i am not using any inbuilt method.
Below is the list combinations this code is tested with:
la = [1,1,1,1,1],la = [1,1,1,1,1,0],la = [5,4,1,2,3],la = [3,6,2,7],la = [1,2,3,4,5],la = [4,5,1,2,3],la = [5,5,4,3,2,1],la = [-1,1,0,0,0],la = [-1,-2,-3,-4], la = [-1,0],la = [-2,-2,-2,-2,0,1,1,1,1]
def findSecmax(la):
mx = la[0]
sec = 0 # or sec = min(la) or we can take (- sys.maxsize)
for i in la:
if i < sec:
sec = i
for i in la:
if i > mx:
mx = i
for i in la:
if i > sec and mx > i:
sec = i
if sec == mx:
return "we have same elements in the list. So max is {}".format(mx)
else:
return mx,sec
print(findSecmax(la))

Best way to replace values in a list based on many indexes

I have list like this:
l = [1,2,3,4,5,6,7,8,9,10]
idx = [2,5,7]
I want to replace values in l with 0, using indexes from idx. For now I do:
for i in idx:
l[i] = 0
This give: l = [1, 2, 0, 4, 5, 0, 7, 0, 9, 10]
Is there better, faster, more pythonic way. This is only small example, but what if I have huge lists?
If you're talking about huge lists, you should really try not to create a new list, as the new list will require space in memory in addition to your input lists.
Now, let's consider the indices that you want to set to 0. These indices are contained in a list (idx), which itself could be just as long as the list with numbers (l). So, if you were to do something like this:
for i in range(len(l)):
if i in idx:
l[i] = 0
it would take O(mn) time, where m is the number of elements in idx and n is the number of elements in l. This is a really slow algorithm.
Now, you really can't do much faster than O(m), seeing as you have to consider every element in idx. But since m is strictly bounded from above by n, it's definitely a better strategy to loop over idx instead:
for i in idx:
l[i] = 0
But let's consider that idx might contain elements that are not valid indices of l (i.e. there is at least one element in idx whose value is greater than the largest index in l). Then, you could do this:
for i in idx:
if i<len(l):
l[i] = 0
or:
for ind in (i for i in idx if i<len(L)):
l[ind] = 0
Now, this makes O(m) comparisons, which could potentially be improved upon. For example, if idx were sorted, then a modified binary search could provide the appropriate slice of idx that has valid indices:
def binSearch(L, idx, i=0, j=None): # note that the list is not sliced, unlike some common binary search implementations. This saves on additional space
if not idx:
return pad
if j==None:
j = len(idx)-1
mid = (i+j)//2
if idx[mid] == len(L)-1:
return mid
elif idx[mid] > len(L)-1:
return binSearch(L, idx, i, mid-1)
else:
return binSearch(L, idx, mid+1, j)
So now, you could replace only the valid indices without any comparisons at all:
for ind in range(binSearch(L, idx)):
l[idx[ind]] = 0
Note that this approach takes O(log m) time to apply binSearch on idx in the first place
This would work if idx were already sorted. However, if that is an invalid assumption, then you might want to sort it yourself, which would cost O(m log m) time, which would be slower than the aforementioned O(m) implementation.
Yet, if idx were sufficiently large, you could try a distributed approach, with multiprocessing:
import multiprocessing as mp
def replace(l, idx):
numWorkers = mp.cpu_count()*2 -1
qIn = mp.Queue(maxsize=len(idx))
qOut = mp.Queue()
procs = [mp.Process(target=slave, args=(L, qIn, qOut)) for _ in range(numWorkers)]
for p in procs:
p.start()
for i in idx:
qIn.put(i)
numFinished = 0
while numFinished != numWorkers:
i = qOut.get()
if i is None:
numFinished += 1
continue
l[i] = 0
def slave(L, qIn, qOut):
for i in iter(qIn.get, None):
if i< len(L):
qOut.put(i)
qOut.put(None)
Of course, you could further improve this by adding the binSearch to the distributed solution as well, but I'll leave that to you.
Don't create another list for index. Instead:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
index = 1
while index < len(l):
if index == 2:
l[index] = 0
elif index == 5:
l[index] = 0
elif index == 7:
l[index] = 0
index += 1
print(l)
You do not have to use "elif" statements if you combine them all on one line with an "or" statement. For example:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
index = 1
while index < len(l):
if (index == 2) or (index == 5) or (index == 7):
l[index] = 0
index += 1
print(l)
I think this is perfectly fine. You could write a list comprehension, like this:
[v if i not in idx else 0 for i, v in enumerate(l)]
Or change it in place by iterating over l
for i, v in enumerate(l):
if i in idx:
l[i] = 0
But I find that harder to read, and very likely slower. I don't think any other solution will beat yours by a significant margin, ignoring CPU caching.

Categories