关于 Python3 语义

跳过

所以我正在做一个问题,计算对数字 1、2、3、...、n 进行排序所需的最小交换次数。

现在我能够解决这个问题,但我必须用语法做一个非常具体的事情才能让它工作。有没有人介意帮助我理解为什么我必须这样做。

我的原始代码是:

def minimumSwaps(arr):
    c = 0
    
    for index in range(len(arr)):
        value = arr[index]
        if not index + 1 == value:
            #new_i = arr.index(index+1)
            print(str(index) + " -> " + str(new_i))
            arr[index], arr[arr.index(index+1)] = arr[arr.index(index+1)], arr[index]
            print(arr)
            c += 1
            
            
    print(arr)
    return c

这段代码输出的问题是:

  1. 在第一次迭代时,它没有实际交换,但它认识到应该交换事物,请参阅输出
  2. 在最后一次迭代中,它不进行交换,但它认识到需要进行交换
  3. 因此它最终没有足够的交换

例子:

输入:[4, 3, 1, 2]

输出:

0 -> 2

[4, 3, 1, 2]

1 -> 3

[4, 3, 1, 2]

2 -> 1

[4, 1, 3, 2]

3 -> 0

[2, 1, 3, 4]

[2, 1, 3, 4]

现在,当我将代码更改为:

def minimumSwaps(arr):
    c = 0
    
    for index in range(len(arr)):
        value = arr[index]
        if not index + 1 == value:
            new_i = arr.index(index+1)
            print(str(index) + " -> " + str(new_i))
            arr[index], arr[new_i] = arr[new_i], arr[index]
            print(arr)
            c += 1
            
            
    print(arr)
    return c

我所做的只是将“arr.index(index+1)”存储在一个 var 中,并在我的交换代码中使用该变量而不是表达式,它完美地工作。

例子:

输入:[4, 3, 1, 2]

输出:

0 -> 2

[1, 3, 4, 2]

1 -> 3

[1, 2, 4, 3]

2 -> 3

[1, 2, 3, 4]

[1, 2, 3, 4]

我被这个难住了,请帮忙。

编辑 1:它实际上运行不完美,当然,我认为只在较大的输入上出现运行时错误。

暗影游侠

这里的问题是操作顺序:

arr[index], arr[arr.index(index+1)] = arr[arr.index(index+1)], arr[index]

右边的顺序没问题=所有这些都在修改任何内容之前发生,因此您最终会得到一个tuple预期值。

的问题是,在左侧的侧=被从左到右,评价懒惰地使(各目标仅在时刻评价为项被分配)arr.index(index+1)调用发生分配给arr[index]操作顺序是(问题的症结见加粗斜体部分):

  1. 右侧tuple构造(做了很多事情,但都没有危险)
  2. arr 加载
  3. index 加载
  4. 执行索引分配,将tuplefrom #1的第一个元素分配arr[index]arr现在有不同的价值!
  5. arr 再次加载
  6. arr.index(index+1)呼吁arr已经发生突变的第4步!
  7. 执行索引分配,将tuplefrom #1的第二个元素分配arr[post_mutation_index]

因此,当赋值给arr[index]意味着arr.index(index+1)返回不同的索引时,您的代码就会出现异常。预先缓存它可以修复它,因为它会查看预突变arr并始终如一地使用它。

为了使事情更清楚,您的代码等效于:

tmp = arr[index], arr[arr.index(index+1)]
arr[index] = tmp[0]
arr[arr.index(index+1)] = tmp[1]

(只是没有命名tmp)。通过这种排序,更清楚的是arr.index(index+1)调用发生在arr已经发生变异之后,允许它arr[index]在搜索分配另一个值时使用的索引之前找到(或找不到)一个值,因为它被分配了一个新值。

您还可以通过确保左侧.index调用发生在任何其他突变之前来使代码工作,例如:

arr[arr.index(index+1)], arr[index] 

但这将是脆弱的代码(如果维护者更改顺序,或向行中添加其他项目,则更难使其正确),并且无论如何预先缓存一次效率较低(您必须执行两次线性扫描arr每次交换,而不仅仅是一次)。

这是有效的,因为现在的顺序是:

  1. 右侧tuple构造(做了很多事情,但都没有危险)
  2. arr 加载
  3. arr.index(index+1)调用unmodified arr,并且在发生任何索引分配后不再调用(好!)
  4. 执行索引分配,将tuplefrom #1的第一个元素分配arr[pre_mutation_index]
  5. arr 再次加载
  6. index 加载
  7. 执行索引分配,将tuplefrom #1的第二个元素分配arr[index]

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章