Python-Merge sort difficulties - python

I attempted to implement a merge sort, here is my code:
def mergeSort(array):
result=[]
n=len(array)
if n==1:
result=array
else:
a=round(n/2)
first=mergeSort(array[0:a])
second=mergeSort(array[a:n])
for i in range(len(first)):
for j in range(len(second)):
if first[i]<second[j]:
result.append(first[i])
i=i+1
else:
result.append(second[j])
j=j+1
return result
a=[5,4,1,8,7,6,2,3]
b=mergeSort(a)
print(b)
Unfortunately, the result turns out to be [1]. What is wrong with my function?

A number of things...
Firstly, this is a recursive function, meaning you cannot create a list within the function, as you did here:
result=[]
This will simply reset your list after every recursive call, skewing your results. The easiest thing to do is to alter the list that is passed as a parameter to merge sort.
Your next problem is that you have a for loop within a for loop. This will not work because while the first for loop iterates over first, the second for loop will iterate over second for every increment of i, which is not what you want. What you need is to compare both first and second and extract the minimum value, and then the next minimum value, and so on until you get a sorted list.
So your for loops need to be changed to the following:
while i < len(first) and j < len(second):
Which leads me to final problem in your code. The while loop will exit after one of the conditions are met, meaning either i or j (one or the other) will not have reached len(first) or len(second). In other words, there will be one value in either first or second that is unaccounted for. You need to add this unaccounted value to your sorted list, meaning you must implement this final excerpt at the end of your function:
remaining = first if i < j else second
r = i if remaining == first else j
while r < len(remaining):
array[k] = remaining[r]
r = r + 1
k = k + 1
Here r represents the index value where the previous while loop broke off. The while loop will then iterate through the rest of the remaining values; adding them to the end of your sorted list.
You merge sort should now look as follows:
def mergeSort(array):
if len(array)==1:
return array
else:
a=round(len(array)/2)
first=mergeSort(array[:a])
second=mergeSort(array[a:])
i = 0
j = 0
k = 0
while i < len(first) and j < len(second):
if first[i]<second[j]:
array[k] = first[i]
i=i+1
k=k+1
else:
array[k] = second[j]
j=j+1
k=k+1
remaining = first if i < j else second
r = i if remaining == first else j
while r < len(remaining):
array[k] = remaining[r]
r += 1; k += 1
return array
I tried to not alter your code as much as possible in order to make it easier for you to understand. However, if your difficulty in understanding what I did persists, try de-bugging your merge sort using multiple print statements so that you can follow the function's progress and see where it goes wrong.

Related

How to find the positions that a number falls in-between in a set of sequential integers

I have an number variable and a sequential set of integers. As the number variable changes, I need to record what positions in the sequential set of integers the number variable falls in-between.
For example, if my set of integers is:
li = [20,21,22,23,24,25,26,27,28,29,30]
And the number variable is:
num = 22.74
My desired output would be the positions in the list that num falls in-between:
2,3
I know that li.index(22) would return the position but only if that exact item is in the list. Haven't been able to come across how to find positions if the number falls in-between items.
I imagine a for loop that compares num to each of the neighboring position items in the list by checking if num falls in between those two integers could solve it. Something like:
for x and the following item in li:
if x < num < the following item:
positionone = x
positiontwo = the following item
else:
continue
Just haven't been able to complete how this works, mainly by getting the next position of the list in replace of "the following item". Maybe there's a better way of figuring this? Any help is much appreciated! Thanks!
You are close with your basic logic. You need to iterate through the list. If you do it using the index, rather than the value, you'll be able to retrieve the positions you want.
Your basic logic is fine.
for idx in range(len(li)-1):
if li[idx] < num < li[idx+1]:
pos1, pos2 = idx, idx+1
Given that, I expect that you can solve the problem of equality as a special case, right?
I'm not going to touch the ambiguities regarding num being integer or not. But I will give you the tool needed to answer your question.
Let lo and hi respectively be the lower and upper limit of your interest. Then, to get the indices of the number between these two is done as such:
indices = [i for i, x in enumerate(li)
if lo < x < hi]
Please note that I use < twice there. If you want the lower bound to be inclusive, replace the first < with <=. Similarly, if you want the upper bound to be inclusive replace the second < with <=. Or very generally:
def indices_between(iterable, lo, hi, include_lo=False, include_hi=False):
lo_req = (lambda x: lo <= x) if include_lo else (lambda x: lo < x)
hi_req = (lambda x: x <= hi) if include_hi else (lambda x: x < hi)
return [i for i, x in enumerate(iterable)
if lo_req(x) and hi_req(x)]
You can compare your number with each element and break loop if current element greater than your number:
for index, current_element in enumerate(li):
if current_element > num:
position_one, position_two = index - 1 if index else index, index
break
else:
position_one, position_two = index, index
A mathematical way of doing this is to subtract the number from the list and check for the two minimum absolute value. If li is a numpy array you can do this in one line:
li = np.array(li)
interval = sorted(np.abs(li-num).argsort()[:2])

iterating over a changeable iterator in every iteration

I am trying to implement the code below, and I'm getting an error of
"index is out of range". I think I am getting the error because the for loop saved the value of the length of the array, while I changed it inside the loop itself.
I can't figure out how to solve it using a for loop.
I did solve it using a recursive way, but it was computationally expensive and I am dealing with billions of words. BTW, the Comp function only returns a pool if the 2 words having the same sentence, so I believe it doesn't effect.
I am using Python-3, pycharm.
def reg(array,n):
f=open(r"C:\Users\Ali\Desktop\love.txt","w")
length= len(array)
if length==1:
return array
k=0
for item in range (length-1):
k+=1
for j in range(k,length):
if Comp(array[item][0],array[j][0])>=n:
f.write(str("\n"))
f.write(str(array[item][1]))
f.write(str("\n"))
f.write(str(array[j+k ][1]))
array[k+j]=array.pop()
length-=1
break
f.close()
pass
Instead of modifying the array, just keep track of your effective last index and break out when you hit it. Something like (untested)
k = 0
last_index = len(array) - 1
for item in range(length - 1):
if item > last_index:
break
k += 1
# ....
array[k+j] = array[last_index]
last_index -= 1
break

Index-Error: string index out of range -

def find_starman(board):
row_number = 0
column_number = 0
for star in board:
for j in range(len(board):
for i in range(len(board):
if star[j][i] == '*':
j += 1
i += 1
return [j,i]
I have no idea, everytime I ran the program, it says it is out of range?
How can I fix this?
(Also, forgot to say the question, when if finds the "*" it returns j+1 and i+1 )
For for loops, you don't need to increment the iterator in the loop body as it will automatically update to the new value after each loop (in this case, an increment of 1 since you're assigning it to iterate over range with steps of 1).
In other words, these lines are unnecessary:
j += 1
i += 1
Furthermore, from your edited question, it seems you'd like to return the coordinates (i+1, j+1) of the found *'s. In that case:
1) If you want to return those coordinates for only the first * you can find, and immediately exit the function, you can do:
if star[j][i] == '*':
return (j+1, i+1)
2) If you want to return the coordinates for all the *'s in your array, you can create new variables (like an empty list) before you build your loop, and for every run of the for loop, store the i+1 and j+1 of the found * as a child tuple/list to that variable (using append). In other words, something like this:
found_coordinates = []
for i in range(len(board)):
for j in range(len(board)):
if star[i][j] == '*':
found_coordinates.append((j+1, i+1))
In any case, your iterators (i and j) are either immediately returned or stored in another object, and should not be modified (using += or something else) inside a for loop.
You should remove j+=1 and i+=1 from your code and modify your return like this: return(j+1, i+1). I think thats what you want to do.
if star[j][i] == '*':
return [j+1,i+1]

Negative number finder in index and occuring

Write a func/on first_neg that takes a (possibly empty) list of
numbers as input parameter, finds the first occurrence of a
nega/ve number, and returns the index (i.e. the posi/on in the
list) of that number. If the list contains no nega/ve numbers or it
is empty, the program should return None. Use while loop (and
not for loop) and your while loop should stop looping once the
first nega/ve number is found.
This is the question my teacher asked me any ideas this what i did:
def first_neg(list):
count = 0
for number in list:
if number < 0:
count += 1
return count
Dosent seem to work properly i just joined 1st post hope can get some help
x = [1,2,3,-5]
def first_neg(list):
count = 0
for number in list:
count += 1 #moved it outside of the if
if number < 0:
return count
print(first_neg(x)) #prints 4
You want to increment count not when you've found the answer but everytime the forloops loops. Note that this method returns 4 which is the fourth item in the list, not the index, Index of the list starts from 0 so to access it would be 3. Take our list x = [1,2,3,-5], -5 is in the fourth slot of the list, but to access it we have to call x[3] since lists starts at 0 indexing.
If you want to return the index of the list where the first negative number is found try this:
x = [1,2,3,-5]
def first_neg(list):
for count, number in enumerate(list):
if number < 0:
return count
print(first_neg(x)) # prints 3
This is because enumerate creates a "pairing" of the item in the list and it's the current count. Enumerate just counts from 0 everytime it gets an item out of the list.
Also as a side note ( I didn't change it in my answer since I wanted you to understand what's going on ). Don't name your variables keywords like list, tuple, int, str... Just a bad idea and habit, it works as you can see but it can cause issues.
Return the index immediately once you encounter the negative element. Increment the index otherwise:
def first_neg(lst):
count = 0
while count < len(lst):
if lst[count] < 0:
return count
count = count + 1
return None
Note : Better if you use enumerate() rather than using extra count variable. The code you mentioned is not written in pythonic way.
You may try this as well:
def first_neg(lst):
res = [i for i,x in enumerate(lst) if x<0]
return None if res == [] else res[0]
The code above can be improved using generators as suggested by #Chris_Rands.

returning indices using a while loop

I'm trying to take an input of a list of numbers and return the list of indices in the original list that contain negative values. I also want to use a while loop. Here is my code so far.
def scrollList2(myList):
negativeIndices = []
i = 0
while i < len(myList):
if myList[i] < 0:
i = i + 1
negativeIndices.append(i)
return negativeIndices
How to I stop the loop and how do i get the indices to return? Right now when I run it, it runs forever (infinite loop) how do I tell it to stop once it hits the last indices of myList?
When you hit your first non-negative number, the if is never entered again and i never gets incremented again. Put the part where you increment i outside the if block.
while i < len(myList):
if myList[i] < 0:
i = i + 1
negativeIndices.append(i)
Assume, the conditional myList[i] < 0 is not true. In that case, i won’t be incremented and nothing else happens either. So you will end up in the next iteration, with the same value of i and the same conditional. Forever, in an endless loop.
You will want to increment i regardless of whether you matched something or not. So you will have to put the increment outside of the if conditional. Furthermore, you want to increment i after appending the index to the list, so you actually append the index you tested, and not the one afterwards:
while i < len(myList):
if myList[i] < 0:
negativeIndices.append(i)
i = i + 1
Also, you would usually use a for loop here. It will automatically take care of giving you all the values of i which you need to index every element in myList. It works like this:
negativeIndices = []
for i in range(len(myList)):
if myList[i] < 0:
negativeIndices.append(i)
range(len(myList)) will give you a sequence of values for every number from zero to the length of the list (not including the length itself). So if your list holds 4 values, you will get the values 0, 1, 2 and 3 for i. So you won’t need to take care of incrementing it on your own.
Another possibility would be enumerate as Foo Bar User mentioned. That function will take a sequence, or list in your case, and will give you both an index and the value at the same time:
negativeIndices = []
for i, value in enumerate(myList):
if value < 0:
negativeIndices.append(i)
As you can see, this completely removes the need to even access the list by its index.
OP wants to use a while loop, so this answer is not exactly on point - but I feel I should point out that many pythonistas will expect something like:
neg_indices = [k for k,v in enumerate(myList) if v < 0]
This is implicit in the other answers, however it might be useful for lurkers and for OP to consider down the road... While certainly does the job as the other answers show, but its 'free' in a list comprehension; plus, there's no chance of an infinite loop here....

Categories