【问题标题】:Numpy Array MergesortNumpy 数组合并排序
【发布时间】:2016-01-20 00:47:56
【问题描述】:

我是 Numpy 的新手,正在尝试使用我在以下位置找到的示例合并排序程序:http://interactivepython.org/runestone/static/pythonds/SortSearch/TheMergeSort.html

但是,当我尝试在 numpy 数组上使用它时:

blist = np.array([54, 26, 93, 17, 77, 31, 44, 55, 20])
mergeSort(blist)

打印出来

[17 17 17 17 20 20 20 20 20]

而不是与普通 python 列表一起使用的预期输出。有谁知道为什么该程序似乎不适用于 numpy 数组,但可以使用其预期输入?

【问题讨论】:

  • NumPy 数组和 Python 列表在很多方面都非常不同。通常,您不应期望为列表编写的代码适用于 NumPy 数组。

标签: python arrays numpy mergesort


【解决方案1】:

正如已经说过的,不期望为列表编写的代码也能泛化到 NumPy 数组。

如果要对 NumPy 数组进行排序,可以使用sorted()

blist = np.array([54, 26, 93, 17, 77, 31, 44, 55, 20])
print(sorted(blist))

问题在于合并函数将新值分配给数组的方式。如果你运行它,你可以看到问题:

def mergeSort(alist):
    print("Splitting ",alist)
    if len(alist)>1:
        mid = len(alist)//2
        lefthalf = alist[:mid]
        righthalf = alist[mid:]

        mergeSort(lefthalf)
        mergeSort(righthalf)
        i=0
        j=0
        k=0
        while i < len(lefthalf) and j < len(righthalf):
            if lefthalf[i] < righthalf[j]:               
                alist[k]=lefthalf[i]
                i=i+1
            else:
                print(lefthalf,"lefthalf before the incorrect assignment")
                print(alist[k],righthalf[j])
                alist[k]=righthalf[j]
                print(lefthalf,"lefthalf after the incorrect assignment")               
                j=j+1
            k=k+1


        while i < len(lefthalf):
            alist[k]=lefthalf[i]         
            i=i+1
            k=k+1

        while j < len(righthalf):
            alist[k]=righthalf[j]
            j=j+1
            k=k+1
print("Merging ",alist)

似乎lefthalf 引用 alist 数组中的值,因此将alist 值分配给righthalf 中的值也会改变lefthalf 同时。

运行此代码块使其更加明显:

alist = np.array([54,26])
mid = len(alist)//2
lefthalf = alist[:mid]
righthalf = alist[mid:]
print(lefthalf,"lefthalf before the incorrect assignment")
alist[0]=righthalf[0]
print(lefthalf,"lefthalf after the incorrect assignment") 

问题已在here 讨论。实际上,您实际上可以通过在lefthalf = alist[:mid] righthalf = alist[mid:]lefthalf = alist[:mid].copy() 等之后添加.copy() 来修复它。

【讨论】:

    【解决方案2】:

    这个问题让我很感兴趣,但我不确定这是否是学习numpy 的好方法。像这样的迭代过程并没有很好地利用数组的独特品质。它只是把它当作一个列表来对待,很明显它不太合适。


    这是一个小列表的运行:

    In [608]: alist=[3,2];arr=np.array(alist)
    In [609]: mergeSort(alist)
    Splitting  [3, 2]
    Splitting  [3]
    Merging  [3]
    Splitting  [2]
    Merging  [2]
    Merging  [2, 3]
    
    In [610]: mergeSort(arr)
    Splitting  [3 2]
    Splitting  [3]
    Merging  [3]
    Splitting  [2]
    Merging  [2]
    Merging  [2 2]
    

    区别在于最后一个合并步骤。


    我修改了代码以添加更多诊断打印

    def mergeSort(alist):
        print("Splitting ",alist)
        if len(alist)>1:
            mid = len(alist)//2
            lefthalf = alist[:mid]
            righthalf = alist[mid:]
    
            mergeSort(lefthalf)
            mergeSort(righthalf)
    
            i=0
            j=0
            k=0
            while i < len(lefthalf) and j < len(righthalf):
                if lefthalf[i] < righthalf[j]:
                    alist[k]=lefthalf[i]
                    print('1',i,j,alist)
                    i=i+1
                else:
                    alist[k]=righthalf[j]
                    print('2',i,j,alist)
                    j=j+1
                k=k+1
    
            while i < len(lefthalf):
                alist[k]=lefthalf[i]
                print('3',i,j,alist)
                i=i+1
                k=k+1
    
            while j < len(righthalf):
                alist[k]=righthalf[j]
                print('4',i,j,alist)
                j=j+1
                k=k+1
        print("Merging ",alist)
    

    运行:

    In [623]: alist=[3,2];arr=np.array(alist)
    In [624]: mergeSort(alist)
    Splitting  [3, 2]
    Splitting  [3]
    Merging  [3]
    Splitting  [2]
    Merging  [2]
    2 0 0 [2, 2]
    3 0 1 [2, 3]
    Merging  [2, 3]
    
    In [625]: mergeSort(arr)
    Splitting  [3 2]
    Splitting  [3]
    Merging  [3]
    Splitting  [2]
    Merging  [2]
    2 0 0 [2 2]
    3 0 1 [2 2]
    Merging  [2 2]
    

    所以它在列表的 1 和 4 处“合并”,对于数组,它在 2 和 3 处“合并”。我们能弄清楚为什么吗?我怀疑if 测试存在差异。


    看起来修复是:

        lefthalf = alist[:mid].copy()
        righthalf = alist[mid:].copy()
    

    正如@duhamp 对数组所指出的,对lefthalf 的更改将更改righthalf。对于列表,lefthalfrighthalfalist 的副本。但是索引alist[:mid] 会返回view,而不是副本。这是列表和数组之间的一个重要区别。

    更重要的是,当您遇到 Python 代码(尤其是 numpy 代码)问题时,它有助于在可疑问题点添加诊断打印。您可以使用调试器,或者像我们一样临时修改代码。无论哪种方式,问题通常是您在编写代码时忽略的小细节。

    【讨论】: