Search code examples
algorithmmergesort

Where is the bug in this merge sort implementation of split inversions


I am trying to count the number of inversions in an array using merge sort.

The idea being that the merge step naturally lends itself to what an inversion is: the ith element being greater than the jth element.

def merge_and_count(x, y, result, count):
    i, j = 0, 0

    while i < len(x) and j < len(y):
        if x[i] > y[j]:
            result.append(y[j])
            j += 1
            count += 1
        else:
            result.append(x[i])
            i += 1

    result += x[i:]
    result += y[j:]

    return result, count

def sort_and_count(array):
    if len(array) == 1:
        return array, 0

    count = 0
    result = []

    mid = len(array)//2

    x = array[:mid]
    y = array[mid:]

    x, c1 = sort_and_count(x)
    y, c2 = sort_and_count(y)

    result, c3 = merge_and_count(x, y, result, count)

    return result, (c1+c2+c3)

def main():
    array = [1,3,5,2,4,6]
    result, count = sort_and_count(array)
    print("there are", count, "split inversion")
    print(result)

However this is printing 2 when I the answer should be 3


Solution

  • There's a bug in your merge_and_count function. It should be:

    if x[i] > y[j]:
         result.append(y[j])
         j += 1
         count += len(x) - i # It's not +1 here
    

    When you append the next element from y to the result, it forms an inversion with all elements from x that haven't been added yet. There're exactly len(x) - i such elements.