Smallest number in a particular part of my list - python

I have a question about python. I have to sort a list of random numbers in a particular way (it's not allowed to use sort()). I'll try to explain:
I have to search for the smallest number, and swap this number with the number at the first position in the list.
Then, I search again for the smallest number, but this time ignore the first number in my list because this one is already sorted. So, I should start searching for the smallest number from the second number (index 1) till the end of the list. The smallest number then found, should be swapped with the second number in the list(so the index 1).
I hope you understand my problem. This is the code I wrote so far, but I get errors and/or the sorting isn't correct.
array = random_integers(10,size=10)
my_list = list(array)
for i in range(len(my_list)):
print my_list
a = min(my_list[i:len(my_list)])
b = my_list.index(a)
my_list[i],my_list[b]=my_list[b],my_list[i]
print my_list
I think there's a problem in my range, and a problem with the
a = min(my_list[i:len(my_list)])
I want to search for the smallest number, but not in the ENTIRE list how can I do this?

The problem occurs on this line:
b = my_list.index(a)
since this searches for the first occurrence of a in all of my_list. If the same number occurs twice, then b will always correspond to the smallest such index, which might be less than i. So you might end up moving a number which has already been sorted.
The obvious thing to try is to slice my_list before calling index:
my_list[i:].index(a)
but note that index will return values between 0 and N-i. We want numbers between i and N. So be sure to add i to the result:
b = my_list[i:].index(a)+i
Thus, the easiest way to fix your code as it presently exists is:
for i in range(len(my_list)):
a = min(my_list[i:])
b = my_list[i:].index(a)+i
my_list[i], my_list[b] = my_list[b], my_list[i]
but notice that min is searching through all the items in my_list[i:] and then the call to index is traversing the same list a second time. You could find b in one traversal like this:
b = min(range(i, N), key=my_list.__getitem__)
Demo:
import numpy as np
array = np.random.random_integers(10,size=10)
my_list = list(array)
N = len(my_list)
for i in range(N):
b = min(range(i, N), key=my_list.__getitem__)
my_list[i], my_list[b] = my_list[b], my_list[i]
print my_list
yields
[3, 10, 9, 6, 5, 3, 6, 8, 8, 4]
[3, 3, 9, 6, 5, 10, 6, 8, 8, 4]
[3, 3, 4, 6, 5, 10, 6, 8, 8, 9]
[3, 3, 4, 5, 6, 10, 6, 8, 8, 9]
[3, 3, 4, 5, 6, 10, 6, 8, 8, 9]
[3, 3, 4, 5, 6, 6, 10, 8, 8, 9]
[3, 3, 4, 5, 6, 6, 8, 10, 8, 9]
[3, 3, 4, 5, 6, 6, 8, 8, 10, 9]
[3, 3, 4, 5, 6, 6, 8, 8, 9, 10]
[3, 3, 4, 5, 6, 6, 8, 8, 9, 10]

If you want the smallest number from a list you can use min(). If you want a part of a list you can use list slicing: my_list[1:]. Put the two together and you get the smallest number from a part of your list. However, you don't need to do this, as you can .pop() from the list instead.
sorted_list = []
while my_list:
n = min(my_list)
sorted_list.append(my_list.pop(my_list.index(n)))
If you're using numpy arrays then instead of my_list.index(min(my_list)) you can use the .argmin() method.
While this type of sorting is good for an introduction, it is not very efficient. You may want to consider looking at the merge sort, and also the Python's built-in timsort.

Related

Removing Duplicate Numbers in a list with a twist

I have looked around and found the
if numbers.count(x) >= 2:
numbers.remove(x)
print(numbers)
but I don't want to use that. I think lazy-ier way is to go like this:
numbers = [1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 2, 5, 12]
numbers.sort()
print(numbers)
duplicate = 0
for each_item in numbers:
if duplicate == each_item:
numbers.remove(each_item)
else:
duplicate += 1
print(numbers)
I, first, sort the list then print it for manual comparison. I add a variable called duplicate and set it to 0. I go thru the loop for each number in the list numbers. If I find the duplicate's value same as a number in the list, I remove it and go thru the loop, else I increase the value by 1 and print the list.
The problem is if there are more than 2 duplicates in the list it doesn't work. Which I don't understand why. I run the code in my head and it should work "flawlessly"?
Thank you for your time sensei's
Current output is;
[1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 12]
[1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 12]
Use a set to make things easy:
numbers = [1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 2, 5, 12]
s = set(numbers)
print(list(s))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 12]
You are changing an iterable as you iterate over it. That causes unpredictable behavior, like "skipping" an item in that list. There's plenty of resources on the internet describing the details (for instance).
Instead, iterate over a copy:
for each_item in numbers[:]:
(...)

How to make ascending sublists in a list of integers go in descending order?

Working on some example questions, the particular one asks to make a function which would take a list and return a new one which would make every ascending sublist in the list go in descending order and leave the descending sublists as they are. For example, given the list [1,2,3,4,5], I need the list [5,4,3,2,1] or given a list like [1,2,3,5,4,6,7,9,8] would return [5,3,2,1,9,7,6,4,8]
Here's what I have so far, but it does not do anything close to what I'd like it to do:
def example3(items):
sublst = list()
for i in items:
current_element = [i]
next_element = [i+1]
if next_element > current_element:
sublst = items.reverse()
else:
return items
return sublst
print (example3([1,2,3,2])) #[[1, 2, 3, 2], [1, 2, 3, 2], [1, 2, 3, 2], [1, 2, 3, 2]]
EDIT:
I feel like people are a little confused as to what I want to do in this case, heres a better example of what I'd like my function to do. Given a list like: [5, 7, 10, 4, 2, 7, 8, 1, 3] I would like it to return [10, 7, 5, 4, 8, 7, 2, 3, 1]. As you can see all the sublists that are in descending order such as ([5,7,10]) gets reversed to [10, 7, 5].
It was a bit challenging to figure out what you need.
I think you want something like as follows:
import random
l = [5, 7, 10, 4, 2, 7, 8, 1, 3]
bl =[]
while True:
if len(l) == 0:
break
r = random.randint(0, len(l))
bl.extend(l[r:None:-1])
l = l[r+1:]
print(bl)
Out1:
[10, 7, 5, 4, 8, 7, 2, 3, 1]
Out2:
[10, 7, 5, 2, 4, 1, 8, 7, 3]
Out3:
[3, 1, 8, 7, 2, 4, 10, 7, 5]
Out4:
[2, 4, 10, 7, 5, 3, 1, 8, 7]
etc.
If you want a specific reverse random list:
import random
loop_number = 0
while True:
l = [5, 7, 10, 4, 2, 7, 8, 1, 3]
bl =[]
while True:
if len(l) == 0:
break
r = random.randint(0, len(l))
bl.extend(l[r:None:-1])
l = l[r+1:]
loop_number += 1
if bl == [10, 7, 5, 4, 8, 7, 2, 3, 1]:
print(bl)
print("I tried {} times".format(loop_number))
break
Out:
[10, 7, 5, 4, 8, 7, 2, 3, 1]
I tried 336 times
The general algorithm is to keep track of the current ascending sublist you are processing using 2 pointers, perhaps a "start" and "curr" pointer. curr iterates over each element of the list. As long as the current element is greater than the previous element, you have an ascending sublist, and you move curr to the next number. If the curr number is less than the previous number, you know your ascending sublist has ended, so you collect all numbers from start to curr - 1 (because array[curr] is less than array[curr - 1] so it can't be part of the ascending sublist) and reverse them. You then set start = curr before incrementing curr.
You will have to deal with the details of the most efficient way of reversing them, as well as the edge cases with the pointers like what should the initial value of start be, as well as how to deal with the case that the current ascending sublist extends past the end of the array. But the above paragraph should be sufficient in getting you to think in the right direction.

How to execute a stepped increase of values in a list?

Let's say I have a list
A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I want to increase every 3rd number by a value of 5 to result in
A = [1, 2, 8, 4, 5, 11, 7, 8, 14, 10]
My gut tells me something along the lines of
A[::3] = [x + 5 for x in A]
OR using the loop below with replace somehow integrated
for num in range(0, len(A), 3):
A = num + 5
Send help...thanks in advance.
You almost had it with your first attempt.
Modify the slice to start at the 3rd element (index 2) per your example, and make sure to read the same slice that you're writing to:
A[2::3] = [x+5 for x in A[2::3]]
i think this will do it
A=[x+5 if i%3==0 else x for i,x in enumerate(A,1)]
You could make a function for it which would account for duplicate values or unsorted values. If my assumptions are not correct, you could easily adjust the function to get your values correct.
A = [1, 2, 3, 4, 5, 6, 10, 8, 9, 7]
def list_modifier(passed_list):
passed_list.sort()
mod_list = list(set(passed_list))
out_list = {i:i if (mod_list.index(i)+1)%3 != 0 else (i+5) for i in mod_list }
passed_list = [out_list[i] for i in out_list]
return passed_list
list_modifier(A)
Returned list looks like this:
[1, 2, 8, 4, 5, 11, 7, 8, 14, 10]

what does "+3" represents in [:index+3] in python?

Here is the problem statement:
return list[index:]+list[:index+3]
I know [:] represents all the elements of the list.
what does this "+3" represents here?
for this line:
list[:index+3]
if index is set to 1, it is the same as
list[:4]
it's a simple sum on a variable, meaning it will read to 3 positions after your index variable
Every element from [index] till the end plus every element from beginning up to (but not including) [index+3]
Let's have a look at an example:
>>> list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> index=2
>>> list[index:]+list[:index+3]
[3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5]
Here index is 2, thus list[index:]+list[:index+3] is exactly the same as list[index:]+list[0:2+3] which is list[index:]+list[0:5]. So, Every element from [2] till the end plus every element from beginning up to (but not including) [5]

swapping one number with two numbers in a list in python

If I am given a list of numbers and I want to swap one of them with the next two numbers.
Is there a way to do this in one shot, without swapping the first number twice?
To be more specific, let's say I have the following swap function:
def swap_number(list, index):
'''Swap a number at the given index with the number that follows it.
Precondition: the position of the number being asked to swap cannot be the last
or the second last'''
if index != ((len(list) - 2) and (len(list) - 1)):
temp = list[index]
list[index] = list[index+1]
list[index+1] = temp
Now, how do I use this function to swap a number with the next two numbers, without calling swap on the number twice.
For example: I have the following list: list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Now, how do I swap 3 with the 4 and 5 in one shot?
The expected output would be
list = [0, 1, 2, 4, 5, 3, 6, 7, 8, 9]
Something like this?
def swap(lis, ind):
lis.insert(ind+2, lis.pop(ind)) #in-place operation, returns `None`
return lis
>>> lis = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> lis = swap(lis, 3)
>>> lis
[0, 1, 2, 4, 5, 3, 6, 7, 8, 9]

Categories

Resources