Merge Sort Index Error - python

def merge (seq, p, q, r):
# n1: length of sub-array [p..q]
n1 = q - p + 1
# n2: length of sub-array [q+1 ..r]
n2 = r - q
# Left and Right arrays
left_arr = []
right_arr = []
for i in xrange(0, n1):
left_arr.append( seq[p+i] )
for j in xrange(0, n2):
right_arr.append( seq[q+j+1] )
j=0
i=0
for k in xrange(p,r+1):
if left_arr[i]<= right_arr[j]:
seq[k]=left_arr[i]
i+=1
else:
seq[k]=right_arr[j]
j+=1
return seq
s = [2,4,5,7,1,2,3,6]
p = 0
q = 3
r = 7
print merge(s,p,q,r)
How it works:
A unsorted sequence s is taken, along with the index numbers where the sequence has to be merged. (p=initial, r = final, q=middle)
Now, left_arr and right_arr are [p,q], [q+1, r] respectively
we take left_arr and right_arr initial values (i=0, j=0). We iterate over the sequence seq...
while iterating we are replacing the values of seq with the sorted values...
The above function is able to sort till last number "7".. at the end, its showing "IndexError". I can use exception handling to catch it and fix but I think... for merge sort, its too much.. Can anyone help me in fixing the code as easy as possible.
I am learning Algorithms.. (following book: Introduction to Algorithms by Thomas H. Cormen)

the problem is that at the last iteration i will be equal to 4 and you are trying to access left_arr[5] which does not exist. you should add a stopping condition when i or j become larger then the size of the corresponding array, and then add all the remaining elements in the other array to seq.
Here is a code that works:
def merge (seq, p, q, r):
# n1: length of sub-array [p..q]
n1 = q - p + 1
# n2: length of sub-array [q+1 ..r]
n2 = r - q
# Left and Right arrays
left_arr = seq[p:n1] #here
right_arr = seq[n1:r+1] #here
j=0
i=0
for k in xrange(p, r+1):
if left_arr[i]<= right_arr[j]:
seq[k]=left_arr[i]
i+=1
if i > n1-1: #here
break
else:
seq[k]=right_arr[j] #here
j+=1
if j > n2-1:
break
if i >= len(left_arr): #from here down
seq[k+1:] = right_arr[j:]
elif j >= len(right_arr):
seq[k+1:] = left_arr[i:]
return seq
s = [2,4,5,7,1,1,1,1]
p = 0
q = 3
r = 7
print merge(s,p,q,r)
I have marked with comments the places where I have edited your code.

The problem with your code is that you're looping over xrange(p, r+1), so during that loop in some iteration the value of either i or j might become equal the value of len(left) or len(right), causing the IndexError eventually.
def merge(seq,p,q,r):
left=seq[p:q+1] #a better way to fetch the list
right=seq[q+1:]
i=0
j=0
#you shuldn't loop over the length of seq as that might make the value of either i or j
# greater than the length of left or right lists respectively.
# so you should only loop until one of the lists is fully exhausted
while i<len(left) and j<len(right):
if left[i] <= right[j] :
seq[i+j]=left[i]
i+=1
else:
seq[i+j]=right[j]
j+=1
#now the while loop is over so either right or left is fully traversed, which can be
#find out my matching the value of j and i with lenghts of right and left lists respectively
if j == len(right):
seq[i+j:]=left[i:] #if right is fully traversed then paste the remaining left list into seq
elif i==len(left): #this is just vice-versa of the above step
seq[i+j:]=right[j:]
print seq
s = [2,4,5,7,1,2,3,6]
p = 0
q = 3
r = 7
merge(s,p,q,r)
output:
[1, 2, 2, 3, 4, 5, 6, 7]

You didn't check the array index while iterating left_arr and right_arr.
You should merge the left part of either array when another array ends.
for k in xrange(p,r+1):
if left_arr[i]<= right_arr[j]:
seq[k]=left_arr[i]
i+=1
else:
seq[k]=right_arr[j]
j+=1
# --------------- add this ----------------
# if any array ends, merge the left elements of the other array
if i >= len(left_arr):
seq[k:] = right_arr[j:]
break
elif j >= len(right_arr):
seq[k:] = left_arr[i:]
break
# --------------- end ----------------
return seq

Related

I was asked to make a program in python to check whether a sorting algorithm is stable or not. Need some help regarding that

So I decided to approach this problem by creating another array (lets say arraycount) with the same length of the input array(lets say arraynumber) which is to be sorted. For the program to really check the stability of the sorting function, I decided to use test cases of integer array of repeated occurrences eg arraynumber = {10,8,7,8,2,2,2,1,6}. I then use a function to calculate the occurrences of the number in the array and store them in the arraycount[] which will result as arraycount={1,1,1,2,1,2,3,1,1}.
Naturally after sorting in increasing order they become arraynumber={1,2,2,2,6,7,8,8,10} and the result of the arraycount decides that its stable or not. Hence if its stable arraycount={1,1,2,3,1,1,1,2,1} otherwise it won't be same.
This is the code I have been working on. Help and feedback is appreciated. Main function and input is not taken yet. I will take another array copy of the original arraycount which will be compared afterward to check if it's retained the original position. Sorry if I made any trivial mistakes. Thank you.
def merge(arr1, l, m, r,arr2):
n1 = m - l + 1
n2 = r- m
# create temp arrays
L = [0] * (n1)
R = [0] * (n2)
# Copy data to temp arrays L[] and R[]
for i in range(0 , n1):
L[i] = arr1[l + i]
for j in range(0 , n2):
R[j] = arr1[m + 1 + j]
# Merge the temp arrays back into arr[l..r]
i = 0 # Initial index of first subarray
j = 0 # Initial index of second subarray
k = l # Initial index of merged subarray
while i < n1 and j < n2 :
if L[i] <= R[j]:
arr1[k] = L[i]
arr2[k]=L[i]
i += 1
else:
arr1[k] = R[j]
arr2[k]=R[j]
j += 1
k += 1
# Copy the remaining elements of L[], if there
# are any
while i < n1:
arr1[k] = L[i]
arr2[k]=L[i]
i += 1
k += 1
# Copy the remaining elements of R[], if there
# are any
while j < n2:
arr1[k] = R[j]
arr2[k]=R[j]
j += 1
k += 1
# l is for left index and r is right index of the
# sub-array of arr to be sorted
def mergeSort(arr1,l,r,arr2):
if l < r:
# Same as (l+r)/2, but avoids overflow for
# large l and h
m = (l+(r-1))/2
# Sort first and second halves
mergeSort(arr1, l, m,arr2)
mergeSort(arr1, m+1, r,arr2)
merge(arr1, l, m, r,arr2)
# This function takes last element as pivot, places
# the pivot element at its correct position in sorted
# array, and places all smaller (smaller than pivot)
# to left of pivot and all greater elements to right
# of pivot
def partition(arr1,low,high,arr2):
i = ( low-1 ) # index of smaller element
pivot = arr1[high] # pivot
for j in range(low , high):
# If current element is smaller than or
# equal to pivot
if arr1[j] <= pivot:
# increment index of smaller element
i = i+1
arr1[i],arr1[j] = arr1[j],arr1[i]
arr2[i],arr2[j]=arr2[j],arr2[i]
arr1[i+1],arr1[high] = arr1[high],arr1[i+1]
arr2[i+1],arr2[high]=arr2[high],arr2[i+1]
return ( i+1 )
# The main function that implements QuickSort
# arr1[] --> Array to be sorted,
# arr2[] --> Second Array of the count to be sorted along with first array
# low --> Starting index,
# high --> Ending index
# Function to do Quick sort
def quickSort(arr1,low,high,arr2):
if low < high:
# pi is partitioning index, arr[p] is now
# at right place
pi = partition(arr1,low,high,arr2)
# Separately sort elements before
# partition and after partition
quickSort(arr1, low, pi-1,arr2)
quickSort(arr1, pi+1, high,arr2)
def countArray(arr1,arr2):
i=0,j=1,k=0
while i<range(len(arr1[])):
while j<range(len(arr1[])):
if arr1[i]==arr1[j]:
arr2[i]=k+1
j=j+1
k=0
i=i+1
arraylist=[]
arraycount=[]
def isStable(arr1,arr2):
k=0
while i < range(len(arr1[])):
if arr[i]==arr2[i]:
k=k+1
i=i+1
if k==len(arr2):
print("Stable sort")
else:
print("Unstable sort")

How to optimize this code to reduce execution time?

The question is here:
https://www.hackerrank.com/challenges/equal-stacks/problem
I am getting a termination error due to time out!
Please help me out.
Algorithm explanation:
Step I.
get the 3 array and reverse them, create a new array out of an existing array with each element is sum of all the previous elements. eg: [3,2,1,1,1] -> [1,1,1,2,3] -> [1,2,3,5,8]
So the 3 new array formed would be [1,2,3,5,8] [2,5,9] [1,5,6,7]
Step II.
Again reverse the array [8,5,3,2,1] [9,5,2] [7,6,5,1]
Step III.
Take the smallest array i.e. [9,5,2] traverse the smallest array and search element in the other 2 array - if the element is existing in other 2 array, STOP there and return the number.
Eg. Here I start with elem - 9 : Which is not existing in other 2 array. Next I start with elem - 5 : it is existing in other 2 array.
Hence the answer is 5.
import os
import sys
def equalStacks(h1, h2, h3):
new_h1=[]
new_h2=[]
new_h3=[]
h1=list(reversed(h1))
h2=list(reversed(h2))
h3=list(reversed(h3))
new_h1.append(h1[0])
new_h2.append(h2[0])
new_h3.append(h3[0])
bol=False
ans=-1
for i in range(len(h1)-1):
new_h1.append(new_h1[i]+h1[i+1])
for i in range(len(h2)-1):
new_h2.append(new_h2[i]+h2[i+1])
for i in range(len(h3)-1):
new_h3.append(new_h3[i]+h3[i+1])
low=min([n1,n2,n3])
if low==n1:
for i in list(reversed(new_h1)):
if (i in list(reversed(new_h2))) and (i in list(reversed(new_h3))):
bol=True
ans=i
break
else:
pass
elif low==n2:
for i in list(reversed(new_h2)):
if (i in list(reversed(new_h1))) and (i in list(reversed(new_h3))):
bol=True
ans=i
break
else:
pass
else:
for i in list(reversed(new_h2)):
if (i in list(reversed(new_h1))) and (i in list(reversed(new_h3))):
bol=True
ans=i
break
else:
pass
if bol==True:
return(print(i))
else:
return(print(0))
if __name__ == '__main__':
n1N2N3 = input().split()
n1 = int(n1N2N3[0])
n2 = int(n1N2N3[1])
n3 = int(n1N2N3[2])
h1 = list(map(int, input().rstrip().split()))
h2 = list(map(int, input().rstrip().split()))
h3 = list(map(int, input().rstrip().split()))
result = equalStacks(h1, h2, h3)
I am getting a timeout error....please help me to optimize this code.
If the stacks aren't the same height, then at least one is shorter than the tallest one. The shortest one can't be made taller, so the tallest one must be made shorter.
Pop an item off the tallest stack.
Repeat until they are all the same height.
One possible solution with itertools:
data = '''
3 2 1 1 1
4 3 2
1 1 4 1
'''
from itertools import accumulate, chain, groupby
# prepare the data
data = [[*map(int, line.split())] for line in data.splitlines() if line.strip()]
max_height = 0
for v, g in groupby( sorted(chain(*[accumulate(stack[::-1]) for stack in data]), reverse=True) ):
if sum(1 for _ in g) == 3:
max_height = v
break
print(max_height)
Prints:
5
Simple solution.
Ping me if you are not able to understand.
Here simply apply greedy approach to remove the minimum height cylinder from stack one by one until we get maximum equal height.
def equalStacks(h1, h2, h3):
h1.reverse()
h2.reverse()
h3.reverse()
i = sum(h1)
j = sum(h2)
k = sum(h3)
while i != 0 or j !=0 or k!= 0:
x = min(i,j,k)
if i > x:
i -= h1.pop()
if j > x :
j -= h2.pop()
if k > x:
k -= h3.pop()
if i == j and j == k:
return i
return 0

Use python to solve a math problem: sum up to a value as close as possible

I have a list of numbers. Now if I set a fixed value V, is it possible for python to divide the list into several groups such that each group's sum is not smaller than V (get those groups as many as possible)?
Ex: if the list is [1,2,3,4,5] and the V is 6, then the result should be [[1,5],[2,3,4]]. Dividing the group means you cannot use the same original item more than once.
There's no limitation on how many items each sublist can contain and also the numbers are not in order (can be some random numbers). Could someone help me out? So far my solution is sum up all the combinations and compare the sums. But I'm pretty sure there should be a more effective solution. Thanks!
My solution: I kinda use this first and do the rest job by my mind, so it doesn't worth a further development.
import itertools
import math
stuff = list(range(10))
v = 6
for L in range(0, len(stuff)+1):
for subset in itertools.combinations(stuff, L):
if math.fsum(subset) > v:
print(subset,math.fsum(subset))
My solution has O(n^2) time complexity. You can sort list in ascending order. Then iterate over list from the end. As You want max number of subsets, then each value greater then V added to array. In other case collect values from right and left corners while achieve sum of subset equals to V:
def get_value_in_dict(d):
return d.get(list(d)[0])
# Implementation for a list of dictionaries like [{'apple':1},{'pear':22},{'hat':23},{'glass':44}]
def sum_up_to_value(stuff, val):
new_stuff = []
sorted_stuff = list(sorted(stuff, key=lambda el: get_value_in_dict(el)))
n = len(stuff)
pointer_r = n - 1
pointer_l = 0
queue = list()
while pointer_r >= pointer_l:
if get_value_in_dict(sorted_stuff[pointer_r]) >= val:
new_stuff.append([sorted_stuff[pointer_r]])
else:
subsum = get_value_in_dict(sorted_stuff[pointer_r])
substuff = []
while pointer_l < pointer_r and subsum < val:
# get from queue
while len(queue) and subsum < val:
temp = queue.pop(0)
subsum += get_value_in_dict(temp)
substuff.append(temp)
# get from input
else:
if subsum < val:
subsum += get_value_in_dict(sorted_stuff[pointer_l])
substuff.append(sorted_stuff[pointer_l])
pointer_l += 1
substuff.append(sorted_stuff[pointer_r])
# returns back smallest elements
while subsum - get_value_in_dict(substuff[0]) >= val:
temp = substuff.pop(0)
queue.append(temp)
subsum -= get_value_in_dict(substuff[0])
if subsum < val:
# add substuff to last element of new_stuff
temp = new_stuff.pop()
new_stuff.append(temp + substuff)
else:
new_stuff.append(substuff)
pointer_r -= 1
return new_stuff # list(map(lambda el: sorted(el, key=lambda el_d: get_value_in_dict(el_d)), new_stuff)) for sorted by value elements in resulting list
If you sort your list, this should get you the desired result, although it's not very beautiful as I have to admit:
stuff = range(10)
l = len(stuff)
v = 6
new_stuff = []
i = 0
active = True
while active:
sub_stuff = []
subsum = 0
while subsum<v:
if i>(l-1):
active = False
break
el = stuff[i]
sub_stuff.append(el)
subsum += el
i += 1
else:
new_stuff.append(sub_stuff)
It just goes through your list and sums element until their sum is 6 or higher and then appends a list of those elements to new_stuff and goes on to find the next list.
Try a test list like:
import numpy as np
stuff = sorted(np.random.randint(0,10,100))

Competition practise task (Python)

Not sure if it's the best title. The explanation of what the program is suposed to do is below, my version only works with the first example but it doesn't work in the second when you get for example 1 1 3 1 1 2 because i can't figure out a good way to handle so much variations especially if K is bigger than 3 and the limit is 50. My version:
N, K, M = map(int, input().split())
niz = list(map(int, input().split()))
nizk = list(range(1, K+1))
izlazi = []
for r in range(0, M):
operacija = list(map(int, input().split()))
index = 0
if operacija[0] == 2:
nizkk = []
for z in range(0, len(nizk)):
if nizk[z] in niz:
continue
else:
izlazi.append(-1)
break
for p in range(0, N):
if niz[p] not in nizkk:
nizkk.append(niz[p])
nizkk.sort()
if nizkk == nizk:
index = p
izlazi.append(index+1)
break
else:
continue
else:
index, repl = map(int, operacija[1:])
niz[index - 1] = repl
print(izlazi)
In the first line of the input there should be N, K, M (1 <= N, M <= 100k, 1 <= K <= 50, you don't need to actually check this the numbers that are tested will always be in those ranges). In the second line of input you put a list of numbers which are the lenght of N you entered earlier. M is the number of operations you will do in the following lines of input. There can be 2 operations. If you enter 1 p v(p = index of number you want to replace, v the number you replace it with) or if you enter 2 it needs to find the shortest array of numbers defined by range(1, K+1) in the list of numbers you entered in line 2 and possibly changed with operation 1. If it doesn't exist it should output -1 if it does it should output lenght of numbers in the array you look in(numbers can be like 2, 1, 3 if you're looking for 1, 2, 3, also if you're looking for 1, 2, 3 etc and you have 2, 1, 1, 3 as the shortest one that is the solution and it's lenght is 4). Also the replacement operation doesnt count from 0 but from 1. So watch out when managing lists.
These are the examples you can input in the program ulaz = input, izlaz = ouput:
I have the following idea:
Min length sequence either starts from first element or does not contain first element and hence equals to min length of the same sequence without first element.
So we have recursion here.
For sequence [1,1,3,2,1,1] and [1,2,3] we will have:
Min length from start element [1,1,3,2,1,1] is 4
Min length from start element __[1,3,2,1,1] is 3
Min length from start element ____[3,2,1,1] is 3
Min length from start element ______[2,1,1] is -1
Can stop here.
Result is minimum for [4,3,3] = 3
You have already implemented the part for min length, if it starts from the first element. Need now extract it as a function and create a recursive function.
Some metacode:
function GetMinLength(seq)
{
minLengthFromFirstElement = GetMinLenthFromFirstElement(seq)
minLengthFromRest = GetMinLength(seq[1:]) //recusive call
return Min(minLengthFromFirstElement, minLengthFromRest )//-1 results should not count, add extra code to handle it
}
Unfortunately I don't know python, but I can provide code on F# in case you need it.
EDIT:
Try this:
N, K, M = map(int, input().split())
niz = list(map(int, input().split()))
nizk = list(range(1, K+1))
izlazi = []
def GetMinLengthStartingFromFirstElement(seq):
nizkk = []
for z in range(0, len(seq)):
if seq[z] in nizk:
continue
else:
return -1
for p in range(0, len(seq)):
if seq[p] not in nizkk:
nizkk.append(seq[p])
nizkk.sort()
if nizkk == nizk:
index = p
return index+1
else:
continue
return -1
def GetMinLength(seq):
if len(seq) == 0:
return -1
else:
curMinLength = GetMinLengthStartingFromFirstElement(seq)
if curMinLength == -1:
return -1
minLengthFromRest = GetMinLength(seq[1:])
if minLengthFromRest > -1:
return min(curMinLength,minLengthFromRest)
else:
return curMinLength;
for r in range(0, M):
operacija = list(map(int, input().split()))
index = 0
if operacija[0] == 2:
minLength = GetMinLength(niz)
izlazi.append(minLength)
else:
index, repl = map(int, operacija[1:])
niz[index - 1] = repl
print(izlazi)

Index Error : list index out of range in python code

This is my Python code for merge sort, and I can't understand why I am getting an IndexError on line 23 .
line 23 does not give an error if put before the for loop but in the for loop it says list index out of range. :(
from math import floor
def merge_sort(a,p,r):
if p < r:
q = (p+r)/2
merge_sort(a,p,q)
merge_sort(a,q+1,r)
merge(a,p,q,r)
def merge(A,p,q,r):
n1 = q - r +1
n2 = r - q
L = []
R = []
#print n1
for i in range (1,n1):
L.append(a[p+i-1])
for j in range (1,n2):
R.append(a[q+j])
L.append(10000)
R.append(10000)
i,j=0,0
for k in range (p,r):
if L[i] <= R [j]: # This is where the error occurs
A[k] = L[i]
i = i + 1
else :
A[k] = R[j]
j = j + 1
a=[1,4,9,8,2,3,8,2,9]
merge_sort(a,1,len(a))
print a
This is not python, this is Pascal with Python syntax.
Here is implementation of merge sorting algo (based on Merge sort wiki article):
def merge_sort(data):
if len(data) <= 1:
return data
middle = len(data) / 2
left = merge_sort(data[0:middle])
right = merge_sort(data[middle:])
return merge(left, right)
def merge(left, right):
result = []
while left or right:
if left and right:
if left[0] <= right[0]:
result.append(left.pop(0))
continue
result.append(right.pop(0))
continue
if left:
result.append(left.pop(0))
elif right:
result.append(right.pop(0))
return result
data = [1, 4, 9, 8, 2, 3, 8, 2, 9]
result = merge_sort(data)
assert len(data) == len(result)
print result
I agree with the commenters/other posters, this script has a Python syntax (apart from indentations that are not the customary 4 spaces...) but is far from Pythonic...
Anyhow, for your problem: it looks like the issue shows up in merge when (p,q,r)=(1,2,3). In that case, n1=0 and n2=1. Looping on range(1,n1) will get you nothing, therefore your L will only have 1 element (the 10000 you append to it outside the loop).
Now, when you reach the for k in range (p,r) loop:
on the first iteration, k=p=1, L[0] <= R[0] and you add 1 to i, so i=1
on the second iteration, you try to access L[i], ie L[1], which isn't defined. Hence, the IndexError.
A few advices:
Learn how to use a debugger. Lots of IDEs come with some implementation, it really helps.
Remember that lists start with an index of 0. It's something you tend to forget when you come from another language.

Categories