计数反转算法的不稳定行为(Python)

时间:2018-08-21 12:05:19

标签: python algorithm

首先,反演是无序列表中的数字对,其中两个数字中的较大者在较小数字的左侧。在以下列表中:[1、3、5、2、4、6]有3个反演:(3,2),(5,2)和(5,4)

这是我计算反转的代码:

def num_inv(array):
    count = count_left = count_right = 0
    if len(array)<=1:
        return 0

    mid = len(array)//2
    left_array = array[:mid]
    right_array = array[mid:]

    count_left = num_inv(array[:mid])
    count_right = num_inv(array[mid:])


    i = j =  k = 0

    while i < len(left_array) and j < len(right_array):
        if left_array[i] <= right_array[j]:
            array[k] = left_array[i]
            i+=1
        else:
            array[k] = right_array[i]
            j +=1
            count += len(left_array[i:])
        k+=1   


    while i <len(left_array):
        array[k] = left_array[i]
        i+=1
        k+=1


    while j <len(right_array):
        array[k] = right_array[j]
        j+=1
        k+=1


    return count + count_left + count_right

在我的测试用例(大多数)中,它给出了正确的结果,但有时是错误的。您能看看一下,告诉我代码有什么问题吗?我花了很多时间进行调试,因此我需要重新看一下代码。

测试用例:t4输出不正确的结果

t1 = [1,3,5,2,4,6]
print("Testing using", t1)
print("Expecting:", 3)
print("Returned:", num_inv(t1))

t2 = [1,5,3,2,4]
print("\nTesting using", t2)
print("Expecting:", 4)
print("Returned:", num_inv(t2))

t3 = [5,4,3,2,1]
print("\nTesting using", t3)
print("Expecting:", 10)
print("Returned:", num_inv(t3))

t4 = [1,6,3,2,4,5]
print("\nTesting using", t4)
print("Expecting:", 5)
print("Returned:", num_inv(t4))

t5 = [1,2,3,4,5,6]
print("\nTesting using", t5)
print("Expecting:", 0)
print("Returned:", num_inv(t5))

2 个答案:

答案 0 :(得分:2)

首先,在第一个left_array[i] <= right_array[j]循环中比较while,然后从right_array[i]复制。这一定是一个错误,但这并不是最重要的。

此更正之后,我在您的代码中添加了一行,即第一个while循环中的诊断print()语句:

while i < len(left_array) and j < len(right_array):
    if left_array[i] <= right_array[j]:
        array[k] = left_array[i]
        i+=1
    else:
        array[k] = right_array[j]
        j +=1
        print("Counting as inversions of", array[k], ":", left_array[i:])
        count += len(left_array[i:])
    k+=1   

然后我运行您的测试t4,并获得以下输出:

Testing using [1, 6, 3, 2, 4, 5]
Expecting: 5
Counting as inversions of 3 : [6]
Counting as inversions of 2 : [6, 3]
Counting as inversions of 4 : [6, 3]
Counting as inversions of 5 : [6, 3]
Returned: 7

明白吗?当您找到一对反向时,就无法像您的代码那样假定left_array的其余部分都由反向(即,大于array[k]的数字)组成。这里,4和5相对于3不会倒置。您必须进行不同的计数。

答案 1 :(得分:0)

类似的事情似乎起作用。

(它会生成实际的反演及其索引,而不仅仅是计数。)

def find_inversions(arr):
    for i, ival in enumerate(arr):
        for j in range(i + 1, len(arr)):
            jval = arr[j]
            if jval < ival:
                yield (i, j), (ival, jval)


for case, expected in (
    ([1, 3, 5, 2, 4, 6], 3),
    ([1, 5, 3, 2, 4], 4),
    ([5, 4, 3, 2, 1], 10),
    ([1, 6, 3, 2, 4, 5], 5),
    ([1, 2, 3, 4, 5, 6], 0),
):
    n = len(list(find_inversions(case)))
    print(case, n, expected)

输出

[1, 3, 5, 2, 4, 6] 3 3
[1, 5, 3, 2, 4] 4 4
[5, 4, 3, 2, 1] 10 10
[1, 6, 3, 2, 4, 5] 5 5
[1, 2, 3, 4, 5, 6] 0 0

编辑:这是另一种不是O(N ^ 2)的方法。例如,速度约为66%。 range(100),而range(500)的速度快2倍。

def find_inversions_2(arr):
    seen_indices = defaultdict(set)
    for index, value in enumerate(arr):
        seen_indices[value].add(index)
        for seen_val in seen_indices:
            if seen_val > value:
                for seen_index in seen_indices[seen_val]:
                    yield (seen_index, index), (seen_val, value)