Number of Inversions Algorithm with Merge Sort - python

I'm trying to complete an assignment of an Algorithmic course offered by UC San Diego on Coursera and I got a problem...
I used merge sort to count inversions by summing the number of elements left in left array (a array) to the count each time you merge an element from the array in the right (b array), but I don't really know why it doesn't give the correct number of inversions sometimes, and it's driving me nuts, here is the code and some test cases:
import sys
def get_number_of_inversions(a, left, right):
number_of_inversions = 0
# print(a)
if right - left <= 1:
return number_of_inversions
mid = (left + right) // 2
number_of_inversions = get_number_of_inversions(a, left, mid)
number_of_inversions = get_number_of_inversions(a, mid, right)
left_side = a[left:mid]
right_side = a[mid:right]
while len(left_side) > 0 and len(right_side) > 0:
if left_side[0] <= right_side[0]:
a[left] = left_side[0]
left += 1
left_side.remove(left_side[0])
else:
a[left] = right_side[0]
left += 1
right_side.remove(right_side[0])
number_of_inversions += len(left_side)
if len(left_side) == 0:
a[left] = right_side[0]
left += 1
else:
a[left] = left_side[0]
left += 1
return number_of_inversions
if __name__ == '__main__':
n = int(input())
a = list(map(int, input().split()))
print(int(get_number_of_inversions(a, 0, len(a))))
Test cases:
6
9 8 7 3 2 1
This one is supposed to give 15, but it gives 12
6
3 1 9 3 2 1
This one is supposed to give 9, but it gives 8

Related

First occurrence of a positive integer using binary search

I've tried to write a code that finds the first occurrence of a positive integer in a sorted array using binary search, but it doesn't work.
Here's the code:
def findFirstOccurrence(arr):
(left, right) = (0, len(arr) - 1)
result = -1
while left <= right:
mid = (left + right) // 2
if 0 < arr[mid]:
result = mid
right = mid - 1
elif 0 > arr[mid]:
right = mid - 1
else:
left = mid + 1
return result
Below code will help.
def findFirstOccurrence(arr):
left,right=0,len(arr)-1
while not((left==right) or (right==left+1)):
mid=(left+right)//2
if arr[mid]>0:
right=mid
else:
left=mid
if arr[left]>0:
return left
if arr[right]>0:
return right
return None
print(findFirstOccurrence([-4,-3,-2,-1,0,1,2,5,3,4,6]))
Output
5
The error comes from the lines
elif 0 > arr[mid]:
right = mid - 1
If arr[mid] is negative, we decrease the right pointer — i.e. we look further to the left. But if the array is sorted in ascending order, then anything further to the left of a negative number is still negative, so we'll never reach a positive number and the program will fail.
What we want to do instead is look to the right, where the positive numbers are. The lines
else:
left = mid + 1
already do that in the case that arr[mid] == 0. Removing the two erroneous lines would allow the case that arr[mid] < 0 to fall through and do what we want.
Final code:
if arr[mid] > 0:
result = mid
right = mid - 1
else:
left = mid + 1

Python recursive Merge Sort by index

I have a question about the Python version of recursive Merge Sort. I completed the basic version, which is only referred by the array, and am now working on the index version. I will sink into the endless loop, but I am not sure where I did wrong. Would you mind sharing some ideas? Thank you in advance.
The successful and non-index version:
def mergesort(x):
# The base case is when the array contains less than 1 observation.
length = len(x)
if length < 2:
return x
else:
# Recursive case:merge sort on the lower subarray, and the upper subarray.
mid = (length) // 2
lower = mergesort(x[:mid])
upper = mergesort(x[mid:])
# merge two subarrays.
x_sorted = merge(lower, upper)
return (x_sorted)
def merge(lower, upper):
nlower = len(lower)
nupper = len(upper)
i, j, k = 0, 0, 0
# create a temp array to store the sorted results
temp = [0] * (nlower + nupper)
# as the lower and upper are sorted, since the base case is the single observation.
# now we compare the smallest element in each sorted array, and store the smaller one to the temp array
# repeat this process until one array is completed moved to the temp array
# store the other array to the end of the temp array
while i < nlower and j < nupper:
if lower[i] <= upper[j]:
temp[k] = lower[i]
i += 1
k += 1
else:
temp[k] = upper[j]
j += 1
k += 1
if i == nlower:
temp[i+j:] = upper[j:]
else:
temp[i+j:] = lower[i:]
return temp
With expected results:
x = random.sample(range(0, 30), 15)
mergesort(x)
[0, 1, 3, 6, 9, 10, 11, 13, 14, 17, 18, 20, 25, 27, 29]
But I will stuck into the endless loop with the index version:
def ms(x, left, right):
# the base case: right == left as a single-element array
if left < right:
mid = (left + right) // 2
ms(x, left, mid)
ms(x, mid, right + 1)
m(x, left, mid, right)
return m
def m(x, left, mid, right):
nlower = mid - left
nupper = right - mid + 1
temp = [0] * (nlower + nupper)
ilower, iupper, k = left, mid, 0
while ilower < mid and iupper < right + 1:
if x[ilower] <= x[iupper]:
temp[k] = x[ilower]
ilower += 1
k += 1
else:
temp[k] = x[iupper]
iupper += 1
k += 1
if ilower == mid:
temp[k:] = x[iupper:right+1]
else:
temp[k:] = x[ilower:mid]
x[left:right+1] = temp
return x
The test data as:
x = random.sample(range(0, 30), 15)
ms(x, 0, 14)
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
<ipython-input-59-39859c9eae4a> in <module>
1 x = random.sample(range(0, 30), 15)
----> 2 ms(x, 0, 14)
... last 2 frames repeated, from the frame below ...
<ipython-input-57-854342dcdefb> in ms(x, left, right)
3 if left < right:
4 mid = (left + right)//2
----> 5 ms(x, left, mid)
6 ms(x, mid, right+1)
7 m(x, left, mid, right)
RecursionError: maximum recursion depth exceeded in comparison
Would you mind providing some insights? Thanks.
Your index version uses a confusing convention whereby left is the index of the first element in the slice and right is the index of the last element. This convention requires error-prone +1/-1 adjustments. Your problem is this: mid as computed is the index of the last element in the left half, but you consider mid to be the first element of the right half: a slice of 2 elements is split into one with 0 and one with 2, hence the infinite recursion. You can fix this problem by changing ms(x, mid, right+1) to ms(x, mid+1, right), etc.
Furthermore, retuning m from function ms makes no sense. You should return x if anything at all.
It is much less error prone for right to be the index one past the last element, just like range specifiers in Python. This way there are no confusing +1/-1 adjustments.
Here is modified version:
def ms(x, left, right):
# the base case: right - left as a single-element array
if right - left >= 2:
mid = (left + right) // 2 # index of the first element of the right half
ms(x, left, mid)
ms(x, mid, right)
m(x, left, mid, right)
return x
def m(x, left, mid, right):
nlower = mid - left
nupper = right - mid
temp = [0] * (nlower + nupper)
ilower, iupper, k = left, mid, 0
while ilower < mid and iupper < right:
if x[ilower] <= x[iupper]:
temp[k] = x[ilower]
ilower += 1
k += 1
else:
temp[k] = x[iupper]
iupper += 1
k += 1
if ilower == mid:
temp[k:] = x[iupper:right]
else:
temp[k:] = x[ilower:mid]
x[left:right] = temp
return x
You would invoke as:
x = random.sample(range(0, 30), 15)
ms(x, 0, len(x))

Hackerrank diagonal difference, what's wrong with my code?

Given a square matrix, calculate the absolute difference between the sums of its diagonals.
For example, the square matrix is shown below:
1 2 3
4 5 6
9 8 9
The left-to-right diagonal =1 + 5 + 9 = 15 . The right to left diagonal =3+5+9=17 . Their absolute difference is |15-17|=2.
Function description
Complete the diagonalDifference function in the editor below. It must return an integer representing the absolute diagonal difference.
diagonalDifference takes the following parameter:
arr: an array of integers .
Input Format
The first line contains a single integer,n , the number of rows and columns in the matrix arr.
Each of the next n lines describes a row,arr[i] , and consists of n space-separated integers arr[i][j] .
Sample Input
3
11 2 4
4 5 6
10 8 -12
Sample Output
15
My code:
def diagonalDifference(arr):
i = 0
j = 0
left = 0
right = 0
for x in range(arr+1):
left += arr[0 + i][0 + j]
right += arr[0 + i][n - j]
i += 1
j += 1
return abs(left - right)
Here's my code. I don't know what's wrong. Please help.
The Question is "Diagonal Difference" on Hackerrank.
EDIT: Second attempt
def diagonalDifference(arr):
left = 0
right = 0
for x in range(len(arr)):
left += arr[0+x][0+x]
right += arr[0+x][len(arr)-x]
return abs(left-right)
I get following error:
right += arr[0+x][len(arr)-x]
IndexError: list index out of range
You cannot access len(arr)-x - for x == 0 this is IndexError :
def diagonalDifference(arr):
left = 0
right = 0
for x in range(len(arr)):
left += arr[0+x][0+x]
right += arr[0+x][len(arr)-x]
return abs(left-right)
arr = [[1,2],[3,4]]
len_arr = len(arr) # len(arr) is 2, you index into arr[0][2-0] fox x==0
# but arr only has arr[0][0] and arr[0][1] for x == 0
You need to sum over:
k[0][0], k[1][1], k[2][2], ..., k[n-1][n-1] where n = len(k) for the forward diag
and
k[0][n-1-0], k[1][n-1-1], k[2][n-1-2], ..., k[n-1][n-1-(n-1)] for the backward diag
Codewise:
def diag (data, reverse=False):
ld = len(data)
if reverse:
return sum(data[i][ld-i-1] for i in range(ld))
else:
return sum(data[i][i] for i in range(ld))
k = [[0,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20],[0,22,23,24,25]]
print (diag(k)) # 64
print(diag(k,True)) # 44
def absDiagDiff(data):
return abs(diag(data)-diag(data,True))
print(absDiagDiff(k)) # 20
#!/bin/python3
import math
import os
import random
import re
import sys
#
# Complete the 'diagonalDifference' function below.
#
# The function is expected to return an INTEGER.
# The function accepts 2D_INTEGER_ARRAY arr as parameter.
#
def diagonalDifference(arr):
diag1=0
diag2=0
for x in range(int(len(arr))):
y=list(reversed(list(range(len(arr[x])))))
diag1+=arr[x][x]
diag2+=arr[x][y[x]]
return abs(diag1-diag2)
if __name__ == '__main__':
fptr = open(os.environ['OUTPUT_PATH'], 'w')
n = int(input().strip())
arr = []
for _ in range(n):
arr.append(list(map(int, input().rstrip().split())))
result = diagonalDifference(arr)
fptr.write(str(result) + '\n')
fptr.close()
If that can help, here's how I would of done it :
n = int(input())
array = []
total1 = 0
total2 = 0
index = 0
for i in range(n):
array = [[] for n in range(n)]
for arrays in array:
arrays += map(int,input().split())
if len(arrays) != n:
print(f"Error, need to be {n} by {n}")
if index == 0:
for arrays in array:
total1 += arrays[index]
index += 1
elif index == n:
for arrays in array:
total2 += arrays[index-1]
index -= 1
print(abs(total1-total2))

implementing merge inversions finder

import sys
def get_number_of_inversions(a, b, left, right):
number_of_inversions = 0
if right - left <= 1:
return number_of_inversions
ave = (left + right) // 2
number_of_inversions += get_number_of_inversions(a, b, left, ave)
number_of_inversions += get_number_of_inversions(a, b, ave, right)
#write your code here
return number_of_inversions
if __name__ == '__main__':
input = sys.stdin.read()
n, *a = list(map(int, input.split()))
b = n * [0]
print(get_number_of_inversions(a, b, 0, len(a)))
this code should be implemented only in 'write your code here' area
and what i was trying to do whole day:
i=left
j=ave
k=left
temp_num=0
while i<ave and j<right:
if a[i]<=a[j]:
b[k]=a[i]
i+=1
else:
b[k]=a[j]
j+=1
temp_num += (ave-i)
k+=1
number_of_inversions+=temp_num
and second try not so smart :
left=a[:ave]
right=a[ave:]
result = list()
i,j = 0,0
inv_count = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
elif right[j] < left[i]:
result.append(right[j])
j += 1
inv_count += (len(left)-i)
result += left[i:]
result += right[j:]
number_of_inversions +=inv_count
most problematic for me is:
program begin running with right index one bigger than actually is
first make recursive calls
program don't give back sub arrays instead returns inversions number only
please give me some suggestions how to implement that code
I am not really sure what you are trying to do
But the problem might be here right - left <= 1 .
I believe it should be right <= left

Find enclosed spaces in 2D array

So, I'm generating an array of spaces, which have the property that they can be either red or black. However, I want to prevent red from being enclosed by black. I have some examples to show exactly what I mean:
0 0 0 0 0 0 0 1
0 1 0 0 0 0 1 0
1 0 1 0 0 0 0 1
0 1 0 0 1 1 1 0
0 0 0 0 1 0 1 0
1 1 1 0 1 1 1 0
0 0 1 0 0 0 0 0
0 0 1 0 0 0 0 0
If red is 0 and black is 1, then this example contains four enclosures, all of which I want to avoid when I generate the array. The inputs I have are the size of the array and the number of 1s I can generate.
How would I go about doing this?
Does this code fits well for you?
Basically I fill a matrix from left to right, from top to bottom.
When I have to assign 0 or 1 to a cell, I check (north and west) if adding a 1 could enclose a 0; in this case I put a 0, else a random 0 or 1.
import sys, random
n = int(sys.argv[1])
m = int(sys.argv[2])
# fill matrix with zeroes
matrix = [[0 for _ in xrange(m)] for _ in xrange(n)]
# functions to get north, south, west and east
# cell wrt this cell.
# If we are going out of bounds, we suppose the matrix
# is sorrounded by 1s.
def get_n(r, c):
if r <= 0: return 1
return matrix[r - 1][c]
def get_s(r, c):
if r >= n - 1: return 1
return matrix[r + 1][c]
def get_w(r, c):
if c <= 0: return 1
return matrix[r][c - 1]
def get_e(r, c):
if c >= m - 1: return 1
return matrix[r][c + 1]
# Checks if the cell is already enclosed by 3 1s.
def enclosed(r, c):
enclosing = get_n(r, c) + get_s(r, c) + get_w(r, c) + get_e(r, c)
if (enclosing > 3): raise Exception('Got a 0 enclosed by 1s')
return enclosing == 3
for r in xrange(n):
for c in xrange(m):
# check west and north
if enclosed(r, c - 1) or enclosed(r - 1, c):
matrix[r][c] = 0
else:
matrix[r][c] = random.randint(0, 1)
print str(matrix[r][c]) + ' ',
print ''
Sample run: python spaces.py 10 10
So you can do the following:
Fill array with zeroes
Randomly select a point
If the condition holds, flip color
Repeat from step 2 or exit
The condition holds for all-zeros array. It is hold on any iteration. So, by induction, it is also true for the final array.
In the step 4 you can decide whether to stop or continue by doing, say N=a*b*1000 iterations or whether the ratio red/black is close to 1. In both cases, the result would be slightly biased since you start from all zeros.
Now, what is the condition. You have to ensure that all black points connected and all red points connected as well. In other words, there's maximum 2 connected clusters. Flipping a color could create more connected clusters, so you flip only when the its number is one or two. You can do the check quite efficiently using Union-Find algorithm, described here.
Edit: if however you want to permit black points to be surrounded by red ones but not vice-versa, you may change the condition to have any number of black clusters but only 0 or 1 of red clusters.
This would be a possible way to check the condition:
def: findStart(myArr):
for i in range(len(myArr)):
for j in range(len(myArr[0])):
if(myArr[i][j] == 0):
return (i,j)
def: checkCon(myArr, number_Ones):
width = len(myArr[0])
height = len(myArr)
pen = [] #A list of all points that are waiting to get a visit
vis = [] #A list of all points that are already visited
x = findStart(myArr)
while(len(pen) != 0): #Visit points as long as there are points left
p = pen.pop() #Pick a point to visit
if p in vis:
#do nothing since this point already was visited
else:
vis.append(p)
x,y = p
#A vertical check
if(x == 0 and myArr[x+1][y] == 0):
pen.append((x+1,y))
elif(x == (height-1) and myArr[x-1][y] == 0):
pen.append((x-1,y))
else:
if(myArr[x-1][y] == 0 and x-1 >= 0):
pen.append((x-1,y))
if(myArr[x+1][y] == 0):
pen.append((x+1,y))
#A horizontal check
if(y == 0 and myArr[x][y+1] == 0):
pen.append((x,y+1))
elif(y == (width-1) and myArr[x][y-1] == 0):
pen.append((x,y-1))
else:
if(myArr[x][y+1] == 0):
pen.append((x,y+1))
if(myArr[x][y-1] == 0 and y-1 >= 0):
pen.append((x,y-1))
print((height*width-number_Ones) == len(vis)) #if true, alle Zeros are connected and not enclosed
To clarify this is just a concept to check the condition. The idea is to visit all connected zeros and see if there are any left (that are not connected). If that is the case, there are some enclosed.
This method also doesn't work when the 1's form a frame around the matrix like this:
1 1 1 1
1 0 0 1
1 0 0 1
1 1 1 1
Again, just a concept :)
The problem has two parts actually. Generating the board state, and then checking if it is correct. I realised that checking the correctness was actually worse than just being sure correct states were always generated. This is what I did:
Note that I have defined self.WallSpaces to be an array equal in length to the height of my array, comprised of integers with the number of bits equal to the width of my array. self.Width and self.Height provide the end indices for the array. Basically, Intersects works by checking all the spaces surrounding a point for 1s, except the direction the space was "built from" (see below) and returning True if any of these are the edge of the array or a 1.
def Intersects(self, point, direction):
if (point[0] > 0):
if (direction != [1, 0] and self.WallSpaces[point[0] - 1] & (1 << point[1]) != 0):
return True
if (point[1] == 0 or self.WallSpaces[point[0] - 1] & (1 << (point[1] - 1)) != 0):
return True
if (point[1] == self.Width or self.WallSpaces[point[0] - 1] & (1 << (point[1] + 1)) != 0):
return True
else:
return True
if (point[0] < self.Height):
if (direction != [-1, 0] and self.WallSpaces[point[0] + 1] & (1 << point[1]) != 0):
return True
if (point[1] == 0 or self.WallSpaces[point[0] + 1] & (1 << (point[1] - 1)) != 0):
return True
if (point[1] == self.Width or self.WallSpaces[point[0] + 1] & (1 << (point[1] + 1)) != 0):
return True
else:
return True
if (point[1] == 0 or (direction != [0, 1] and self.WallSpaces[ point[0] ] & (1 << (point[1] - 1)) != 0)):
return True
if (point[1] == self.Width or (direction != [0, -1] and self.WallSpaces[ point[0] ] & (1 << (point[1] + 1)) != 0)):
return True
return False
The directions GPacW.Left, GPacW.Right, GPackW.Up, and GPacW.Down represent the cardinal directions for movement. This function works by constructing "walls" in the array from random points, which can turn in random directions, ending when they have intersected twice.
def BuildWalls(self):
numWalls = 0
directions = [ [GPacW.Left, GPacW.Right], [GPacW.Up, GPacW.Down] ]
start = [ random.randint(0, self.Height), random.randint(0, self.Width) ]
length = 0
horizontalOrVertical = random.randint(0, 1)
direction = random.randint(0, 1)
d = directions[horizontalOrVertical][direction]
intersected = False
while (numWalls < self.Walls):
while (start == [0, 0] or start == [self.Height, self.Width] or self.Intersects(start, d)):
start = [ random.randint(0, self.Height), random.randint(0, self.Width) ]
if (length == 0):
horizontalOrVertical = not horizontalOrVertical
direction = random.randint(0, 1)
length = random.randint(3, min(self.Height, self.Width))
d = directions[horizontalOrVertical][direction]
if (self.WallSpaces[ start[0] ] & (1 << start[1] ) == 0):
self.WallSpaces[ start[0] ] |= 1 << start[1]
numWalls += 1
length -= 1
if (0 <= (start[0] + d[0]) <= self.Height and 0 <= (start[1] + d[1]) <= self.Width):
start[0] += d[0]
start[1] += d[1]
else:
start = [0,0]
if (self.Intersects(start, d)):
if (intersected):
intersected = False
start = [0,0]
length = 0
else:
intersected = True
return

Categories

Resources