【问题标题】:Variable's value changing unexpectedly during recursion (MERGE SORT)递归期间变量的值意外更改(合并排序)
【发布时间】:2020-04-19 16:18:02
【问题描述】:

给定一个输入数组:

[49, 86, 78]

我的代码应该返回:

[[49,1],[78,2],[86,1]]

也就是说,它应该按照每个子数组中的第一个数字对数组进行排序,第二个将作为索引供以后使用。我决定使用归并排序进行排序。这是我的代码(我知道它很难看,但请运行一次):

def merge_sort(array):
    print("array>>>", array)
    if len(array[:,0]) == 1:
        print('going back')
        return
    arrL = array[0:int(len(array)/2),:]
    arrR = array[int(len(array)/2):,:] 
    print('left>>', arrL, 'right>>', arrR)
    print("*****************")
    print('into left')
    merge_sort(arrL)
    print("*****************")
    print('into right')
    merge_sort(arrR)
    print("*****************")
    print('into MERGE')
    merge(array, arrL, arrR)
    print('going back')

def merge(A, arrL, arrR):
    print("array ", A, "left", arrL, "right", arrR)
    i = 0
    while len(arrL[:,0]) is not 0 and len(arrR[:,0]) is not 0:
        print('ARRAYLEFT>>', arrL, 'ARRAYRIGHT>', arrR)
        if arrR[0,0] < arrL[0,0]:
            print(arrR[0,0], 'is less than', arrL[0,0])
            A[i] = arrR[0]
            arrR = arrR[1:,:]
            print('A>>', A, 'arrR>>', arrR, 'ARRAYLEFT>>', arrL)
        else:
            print(arrL[0,0], 'is less than', arrR[0,0])
            A[i] = arrL[0]
            arrL = arrL[1:,:]
            print('A>>', A, 'arrL>>', arrL)
        i += 1

    print('ARRAYLEFT>>', arrL, 'ARRAYRIGHT>>', arrR)

    if len(arrL[:,0]) == 0 :
        print('left exhaused')
        print('A>>', A, 'arrR>>', arrR)
        A[i:,:] = arrR
        print('A>>', A, 'arrR>>', arrR)

    elif len(arrR[:,0]) == 0:
        print('right exhaused')
        print('A>>', A, 'arrL>>', arrL)
        A[i:,:] = arrL
        print('A>>', A, 'arrL>>', arrL)

    print('after merge', A)
    return 

import numpy as np
np.random.seed(1)
input_array = [np.random.choice(np.arange(100)) for _ in range(5)]
print(input_array)

输出:

[37, 12, 72, 9, 75]

那么,

new_array = np.array(list( map(list, zip(input_array, [x for x in range(len(input_array)) ])) ))
print(new_array)

输出:

array([[37,  0],
       [12,  1],
       [72,  2],
       [ 9,  3],
       [75,  4]])

最后,

merge_sort(new_array)

给出以下内容(我只添加了感兴趣的部分)。 请注意,一旦我们转到into MERGE,一旦 arrR 变为 [],arrL 的值就会变为 arrR 的值,代码中用 &lt;&lt;&lt;=== 符号标记:

array>>> [[37  0]
 [12  1]
 [72  2]
 [ 9  3]
 [75  4]]
left>> [[37  0]
 [12  1]] right>> [[72  2]
 [ 9  3]
 [75  4]]
*****************
into left
array>>> [[37  0]
 [12  1]]
left>> [[37  0]] right>> [[12  1]]
*****************
into left
array>>> [[37  0]]
going back
*****************
into right
array>>> [[12  1]]
going back
*****************
into MERGE
array  [[37  0]
 [12  1]] left [[37  0]] right [[12  1]]
ARRAYLEFT>> [[37  0]] ARRAYRIGHT>> [[12  1]]  <<<=== # arrL = [[37  0]]
12 is less than 37
A>> [[12  1]
 [12  1]] arrR>> [] ARRAYLEFT>> [[12  1]]
ARRAYLEFT>> [[12  1]] ARRAYRIGHT>> []       <<<=== # arrL = [[12  1]]
right exhaused
A>> [[12  1]
 [12  1]] arrL>> [[12  1]]
A>> [[12  1]
 [12  1]] arrL>> [[12  1]]
after merge [[12  1]
 [12  1]]
going back
*****************
## .... and so one....

每当代码进入into MERGEarrR = [] 时,似乎都会发生此错误,因此在此之后

print(new_array)

输出:

array([[ 9,  3],
       [ 9,  3],
       [ 9,  3],
       [ 9,  3],
       [75,  4]])

这显然是错误的答案。请帮助我理解我在哪里犯了错误......我已经盯着代码和输出看了好 20 分钟,但似乎找不到它。任何建议/批评都将受到高度赞赏。

【问题讨论】:

  • 抱歉添加了这么多打印语句...我试图调试代码但无济于事
  • 恕我直言,您使此操作过于复杂,对单行解决方案感兴趣(不导入库)?

标签: python recursion mergesort


【解决方案1】:

这有点难以理解,主要是因为不同地方的打印信息相似。这个问题似乎与A[0]arrL 是同一个数组这一事实有关。普通的 python 列表不会出现这种情况,但 numpy 似乎做了某种优化来节省内存。所以当你修改A[i] = arrR[0]的时候,你基本上就分配了arrL = arrR。 除此之外,合并排序算法的实现似乎很不寻常。您既不需要 NumPy 也不需要二维数组,只需一个数字列表即可排序。 如果你只想要一个排序列表,你可以使用内置的实现,sorted(array)。 最后,我建议您习惯调试器(IDE 中的调试器,或 PDB https://realpython.com/python-debugging-pdb/)而不是打印语句,这是理解代码在做什么的更有效的方法。一开始可能很难,但相信我,它会带来回报并帮助您作为开发人员取得进步。注意安全。

【讨论】:

  • 因此,要解决此问题,您可以添加 arrL = array[0:int(len(array)/2),:].copy()(显然也适用于 arrR),但我建议您重写代码以使用普通列表,并使其更清晰,这样您即使没有打印也可以看到代码在做什么。
  • 非常感谢您的回答。还要感谢有关使用调试器的提示...我对 python 还是很陌生,所以我不知道存在这样的工具。我非常感谢并将了解它...再次感谢
  • That would not be the case with plain python lists.. 需要注意的一点,如果不是deep copy,Python 列表的行为也可能与此类似。
  • @EmilMGeorge 感谢您指出这一点,我想说的是@Yatin 使用了array[0:1, :],它必须在list[0:1][:] 的情况下进行复制,实际上是id(A[0]) != id(arrL),但是它仍然修改两个数组。 Numpy 在下面做着某种魔法,而我也没有意识到这一点。
  • @AlexandrTatarinov 是的,您对 numpy 的看法是正确的。看来id(array[0]) == id(array[1])。这太有趣了。必须看看 numpy 是如何做到的。为了澄清我之前的评论,我的意思是listb = lista[0:1][:] 只会制作lista 的浅拷贝。执行listb[0] += [...] 也会改变lista[0]。但是,是的,我同意在这种情况下,列表应该可以正常工作,因为在这里我们将分配另一个列表(=)而不是变异(+=)。我希望它更像是一种相关的注释类型的评论。 :)
【解决方案2】:

到处添加 .copy() 解决了我的问题。

def merge(A,arrL,arrR):
    i = 0
    while len(arrL[:,0]) is not 0 and len(arrR[:,0]) is not 0:
        if arrR[0,0] < arrL[0,0]:
            A[i] = arrR[0].copy()
            arrR = arrR[1:,:].copy()
        else:
            A[i] = arrL[0].copy()
            arrL = arrL[1:,:].copy()
        i+=1

    if len(arrL[:,0]) == 0 :
        A[i:,:] = arrR.copy()

    elif len(arrR[:,0]) == 0:
        A[i:,:] = arrL.copy()

    return 

def merge_sort(array):
    if len(array[:,0]) == 1:
        return
    arrL = array[0:int(len(array)/2),:].copy()
    arrR = array[int(len(array)/2):,:] .copy()
    merge_sort(arrL)
    merge_sort(arrR)
    merge(array,arrL,arrR)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-02
    • 2021-11-06
    • 2019-01-22
    • 1970-01-01
    相关资源
    最近更新 更多