What is wrong with my sorting algorithm? - python

I am a beginner programmer and I've been trying to create my own sorting algorithm in Python, I don't understand why it outputs only some of the numbers that were present in the input. I put debug prints everywhere to understand the problem but still got nothing. The code should find and move to the final array the biggest number of the input array, and do that until the input array it's empty, but it seems to stop at some point. There was a person with a similar problem but the solution did not apply to me as well. This is the code:
array = [3, 6, 25, 4, 5, 24, 7, 15, 5, 2, 0, 8, 1] #just random numbers
output = []
while(len(array) > 0):
maximum = 0
for x in array:
maximum = max(maximum, x)
output.append(maximum)
tempArray = []
for x in array:
temp = array.pop()
if(temp < maximum):
tempArray.append(temp)
array = tempArray
print(output)

The problem is here:
for x in array:
temp = array.pop()
You're modifying the same list that you're iterating over. That's going to cause trouble.

Consider what happens when 5 is the maximum (and there are two 5s in the input.) One 5 gets added to output, the rest of the 5s are never added to tempArray.

To diagnose, put some debug prints in the loop, such as print(output, array) at the end of the outer loop. And maybe more in the inner loop. After seeing the problem (removing two things from array each inner iteration, this works.
array = [3, 6, 25, 4, 5, 24, 7, 15, 5, 2, 0, 8, 1] #just random numbers
output = []
while(array):
maximum = 0
for x in array:
maximum = max(maximum, x)
output.append(maximum)
tempArray = []
for x in array:
if(x < maximum):
tempArray.append(x)
array = tempArray
print(output)
There are, of course, easier and better ways to delete the max from array, and only delete one copy of max instead of all.

Related

Python not iterating over array with for loop [duplicate]

This question already has answers here:
Python invert every two elements of list
(5 answers)
Closed 3 months ago.
Write a program that fills an array of 10 elements with random numbers from 1 to 10, and then swaps the first element with the second, the third with the fourth, and so on. Display the original and transformed array
Here is my solution, but Python doesn't want to sort the array and it stays the same:
from random import randint
numbers = []
for i in range(10):
numbers.append(randint(1, 10))
print(numbers)
a = 0
for a in range(10):
numbers[-1], numbers[i] = numbers[i], numbers[-1]
a = a + 2
print(numbers)
I have tried replacing elements with a loop by numbers[a] = numbers[a+1] , But I kept getting the error:
IndexError: list index out of range
There's a couple things here:
1: as #bereal said, range() has a tird optional step argument, and I've never seen a better time to use it. Check out the documentation for range() https://docs.python.org/3/library/functions.html#func-range
2: I see you reference numbers[-1] even though I think you mean number[-i], it will still reference numbers[-1] on the first iteration, thereby giving an error.
You can make the swap when its not an even number like this:
for a in range(10):
if a % 2 == 1:
tempNumber = numbers[a]
numbers[a] = numbers[a-1]
numbers[a-1] = tempNumber
And then you will have
First output:
[9, 7, 4, 7, 4, 3, 1, 9, 9, 9]
Final output:
[7, 9, 7, 4, 3, 4, 9, 1, 9, 9]

Python ~ First Covering Prefix in Array

So I recently gave an online interview for a job. Although my expertise are networks and cyber security.
I came across this question:
Write a function which takes an array of integers and returns the
first covering prefix of that array. The "first covering prefix" of an
array, A, of length N is the smallest index P such that 0 <= P <= N
and each element in A also appears in the list of elements A[0]
through A[P]. For example, the first covering prefix of the following
array: A = [5, 3, 19, 7, 3, 5, 7, 3] is 3, because the elements from
A[0] to A[3] (equal to [5, 3, 19, 7]) contains all values that occur
in array A.
Although I am not a programmer (chose python3 for the interview),
I would like someone to explain the logic behind this.
Just wanting to learn, its been bugging me for a day now.
You can iterate all elements, if not already seen (use a set to keep track efficiently), update P:
A = [5, 3, 19, 7, 3, 5, 7, 3]
S = set()
P = 0 # you could set -1/None as default to account for empty lists?
for i, item in enumerate(A): # iterate elements together with indices
if item not in S: # if we haven't seen this element yet
P = i # update P as the current index
S.add(item) # add the element to the set
print(P)
output: 3

How could I indexing python list of lists without concatenating?

Assume I have input like this:
bl = [[0,1,2,3,4,5], [6,7,8,9], [10,11,12,13]]
idx = [10, 6, 3, 4, 9, 2]
and I want the output to be like this:
out = [[3,4,2], [6,9], [10]]
The logic behind is like this:
first I can view the lb_cat as some concatenated version:
lb_cat = [0,1,2,3,4,5,6,7,8,9,10,11,12,13]
Then index from the concatenated list:
pick = lb_cat[idx] = [10, 6, 3, 4, 9, 2]
At last, assigned the picked elements back to each group to get output of
out = [[3,4,2], [6,9], [10]]
The difficulty is that I cannot use operations like concatenated, since my input is not standard python list and it does not support concatenation operations.
What I want to do is to pick from a list of objects with the indices from the 'concatenated' view, but I actually pick from each of the elements of the list lb.
How could I do that in an efficient python way?
===========
Edit:
I have implemented a slow version, like this:
import numpy as np
bl = [[0,1,2,3,4,5], [6,7,8,9], [10,11,12,13]]
idx = [10, 6, 3, 4, 9, 2]
milestones = np.cumsum(np.array([len(el) for el in bl])).tolist()
milestones = [0,] + milestones
res = [[] for _ in bl]
for ix in idx:
for i, m in enumerate(milestones):
if ix < m:
res[i-1].append(ix)
break
This works for me, but it is too slow. Do I have a method to make it faster?
I am not sure if I understood your problem correctly, but I hope this will serve as a sufficient answer.
x=0 //to count the total number of numbers with concatination
out = [] //to store final result
for i in bl:
temp = []
for j in i:
if x in idx: //to check if you want xth indexed element
temp.append(j)
x+=1
if len(temp)>0: //only append if has an index in idx, i am not sure if you want this
out.append(temp)
print(out) //to print final output

Given an odd length list of values in Python, how can I swap all values other than the final value in the list?

In regards to Python 2.7.12 (disclaimer: I understand Python2 is being phased out to Python3, but the course I'm taking started us here, perhaps to understand older code bases):
I have a list of integers whom I'd like to swap each with their neighboring value. So far, this works great for lists that are even in the number of integers they contain, however when the list length is odd, it's not so easy to simply swap each value, as the number of integers is uneven.
Giving the following code example, how can I swap all values other than the final value in the list?
arr = [1, 2, 3, 4, 5]
def swapListPairs(arr):
for idx, val in enumerate(arr):
if len(arr) % 2 == 0:
arr[idx], arr[val] = arr[val], arr[idx] # traditional swap using evaluation order
else:
arr[0], arr[1] = arr[1], arr[0] # this line is not the solution but where I know I need some conditions to swap all list values other than len(arr)-1, but am not sure how to do this?
return arr
print swapListPairs(arr)
Bonus Points to the ultimate Pythonic Master: How can this code be modified to also swap strings? Right now, I can only use this function using integers and am very curious how I can make this work for both int and str objects?
Thank you so greatly for any insight or suggestions to point me in the right direction! Everyone's help at times here has been invaluable and I thank you for reading and for your help!
Here's a shorter, probably faster way based on slice assignment:
def swap_adjacent_elements(l):
end = len(l) - len(l) % 2
l[:end:2], l[1:end:2] = l[1:end:2], l[:end:2]
The slice assignment selects the elements of l at all even indices (l[:end:2]) or all odd indices (l[1:end:2]) up to and excluding index end, then uses the same kind of swapping technique you're already using to swap the slices.
end = len(l) - len(l) % 2 selects the index at which to stop. We set end to the closest even number less than or equal to len(l) by subtracting len(l) % 2, the remainder when len(l) is divided by 2.
Alternatively, we could have done end = len(l) & ~1, using bitwise operations. That would construct an integer to use as a mask (~1), with a 0 in the 1 bit and 1s everywhere else, then apply the mask (with &) to set the 1 bit of len(l) to 0 to produce end.
This is easier to do without enumerate. Note that it never, ever makes decisions based on the contents of arr; that is what makes it work on anything, not just a pre-sorted list of integers starting from 1.
for i in range(len(arr)//2):
a = 2*i
b = a+1
if b < len(arr):
arr[a], arr[b] = arr[b], arr[a]
Exercise for you: is the if actually necessary? Why or why not?
You could iterate through the length of the list with a step of two and try to swap values (and except index errors).
def swap_list_pairs(arr):
for index in range(0, len(arr), 2):
try:
arr[index], arr[index+1] = arr[index+1], arr[index]
except IndexError:
pass
return arr
This will work for all data types.
As Copperfield suggested, you could get rid of the try-except-clause:
def swap_list_pairs(arr):
for index in range(1, len(arr), 2):
arr[index-1], arr[index] = arr[index], arr[index-1]
return arr
Similar to #user2357112 but I prefer it this way:
arr[1::2], arr[:-1:2] = arr[:-1:2], arr[1::2]
Demo:
>>> arr = [1, 2, 3, 4, 5]
>>> arr[1::2], arr[:-1:2] = arr[:-1:2], arr[1::2]
>>> arr
[2, 1, 4, 3, 5]
>>> arr = [1, 2, 3, 4, 5, 6]
>>> arr[1::2], arr[:-1:2] = arr[:-1:2], arr[1::2]
>>> arr
[2, 1, 4, 3, 6, 5]
looks like : and :: confuse you a little, let me explain it:
Sequences type of object in python such as list, tuple, str, among other provide what is know as a slice, it come in 2 flavors:
Slicing: a[i:j] selects all items with index k such that i <= k < j. When used as an expression, a slice is a sequence of the same type. This implies that the index set is renumbered so that it starts at 0.
Extended slicing with a third “step” parameter: a[i:j:k] selects all items of a with index x where x = i + n*k, n >= 0 and i <= x < j.
In both cases i, j and/or k can be omitted and in that case suitable values are used instead
Some examples
>>> arr = [10, 20, 30, 40, 50, 60, 70]
>>> arr[:]
[10, 20, 30, 40, 50, 60, 70]
>>> arr[:3]
[10, 20, 30]
>>> arr[1:3]
[20, 30]
>>> arr[1::2]
[20, 40, 60]
>>> arr[::2]
[10, 30, 50, 70]
>>>
the working of this can also be illustrated with the following function
def the_slice(lista, ini=None, end=None, step=None):
result=[]
if ini is None:
ini = 0
if end is None:
end = len(lista)
if step is None:
step = 1
for index in range(ini,end,step):
result.append( lista[index] )
return result
>>> the_slice(arr,step=2) # arr[::2]
[10, 30, 50, 70]
>>> the_slice(arr,ini=1,step=2) # arr[1::2]
[20, 40, 60]
>>>

How to get values in list at incremental indexes in Python?

I'm looking at getting values in a list with an increment.
l = [0,1,2,3,4,5,6,7]
and I want something like:
[0,4,6,7]
At the moment I am using l[0::2] but I would like sampling to be sparse at the beginning and increase towards the end of the list.
The reason I want this is because the list represents the points along a line from the center of a circle to a point on its circumference. At the moment I iterate every 10 points along the lines and draw a circle with a small radius on each. Therefore, my circles close to the center tend to overlap and I have gaps as I get close to the circle edge. I hope this provides a bit of context.
Thank you !
This can be more complicated than it sounds... You need a list of indices starting at zero and ending at the final element position in your list, presumably with no duplication (i.e. you don't want to get the same points twice). A generic way to do this would be to define the number of points you want first and then use a generator (scaled_series) that produces the required number of indices based on a function. We need a second generator (unique_ints) to ensure we get integer indices and no duplication.
def scaled_series(length, end, func):
""" Generate a scaled series based on y = func(i), for an increasing
function func, starting at 0, of the specified length, and ending at end
"""
scale = float(end) / (func(float(length)) - func(1.0))
intercept = -scale * func(1.0)
print 'scale', scale, 'intercept', intercept
for i in range(1, length + 1):
yield scale * func(float(i)) + intercept
def unique_ints(iter):
last_n = None
for n in iter:
if last_n is None or round(n) != round(last_n):
yield int(round(n))
last_n = n
L = [0, 1, 2, 3, 4, 5, 6, 7]
print [L[i] for i in unique_ints(scaled_series(4, 7, lambda x: 1 - 1 / (2 * x)))]
In this case, the function is 1 - 1/2x, which gives the series you want [0, 4, 6, 7]. You can play with the length (4) and the function to get the kind of spacing between the circles you are looking for.
I am not sure what exact algorithm you want to use, but if it is non-constant, as your example appears to be, then you should consider creating a generator function to yield values:
https://wiki.python.org/moin/Generators
Depending on what your desire here is, you may want to consider a built in interpolator like scipy: https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy.interpolate.interp1d
Basically, given your question, you can't do it with the basic slice operator. Without more information this is the best answer I can give you :-)
Use the slice function to create a range of indices. You can then extend your sliced list with other slices.
k = [0,1,2,3,4,5,6,7]
r = slice(0,len(k)//2,4)
t = slice(r.stop,None,1)
j = k[r]
j.extend(k[t])
print(j) #outputs: [0,4,5,6,7]
What I would do is just use list comprehension to retrieve the values. It is not possible to do it just by indexing. This is what I came up with:
l = [0, 1, 2, 3, 4, 5, 6, 7]
m = [l[0]] + [l[1+sum(range(3, s-1, -1))] for s in [x for x in range(3, 0, -1)]]
and here is a breakdown of the code into loops:
# Start the list with the first value of l (the loop does not include it)
m = [l[0]]
# Descend from 3 to 1 ([3, 2, 1])
for s in range(3, 0, -1):
# append 1 + sum of [3], [3, 2] and [3, 2, 1]
m.append(l[ 1 + sum(range(3, s-1, -1)) ])
Both will give you the same answer:
>>> m
[0, 4, 6, 7]
I made this graphic that would I hope will help you to understand the process:

Categories