打乱一个列表并返回一个副本

11
我想对一个数组进行洗牌,但我找到的方法都是像random.shuffle(x)这样的方式,来自Python中随机打乱字符串列表的最佳方法
我能做类似于:
import random
rectangle = [(0,0),(0,1),(1,1),(1,0)]
# I want something like
# disorderd_rectangle = rectangle.shuffle

现在我只能逃避

disorderd_rectangle = rectangle
random.shuffle(disorderd_rectangle)
print(disorderd_rectangle)
print(rectangle)

但它返回

[(1, 1), (1, 0), (0, 1), (0, 0)]
[(1, 1), (1, 0), (0, 1), (0, 0)]

那么原始数组也会被改变。我如何创建另一个随机排列的数组而不改变原来的数组?


以下是一个类似问题的几种解决方法:https://dev59.com/8mMm5IYBdhLWcg3wZ-MX#17649901 - Anton Tarasenko
5个回答

18

人们在这里建议使用深拷贝(deepcopy),这肯定是过度的。你可能不介意列表中的对象是相同的,你只想打乱它们的顺序。为此,列表提供直接浅复制(shallow copying)。

rectangle2 = rectangle.copy()
random.shuffle(rectangle2)

关于你的误解:请阅读http://nedbatchelder.com/text/names.html#no_copies


1
仅因为您将列表称为列表,而其他人仍在称其为数组,所以我点赞了。 - Bhargav Rao
LOL。比什么都好。但是说真的,这表明了对Python对象模型的深刻误解。数组在内存中是连续的,因此人们自然希望进行深度复制。对于我们知道如何工作的人来说,列表只是“表面上连续”的,因此自然的复制就足够了。 - Veky

4
使用 copy.deepcopy 创建数组的副本,对副本进行洗牌。
c = copy.deepcopy(rectangle)
random.shuffle(c)

1
那么就没有像.shuffle方法这样的东西吗? - ZK Zhao
random.shuffle 是用于原地对序列 x 进行洗牌的函数。如果你想要洗牌一个副本,需要先复制一份。仅仅给变量赋予新的名称并不会创建副本。 - user1907906

4

使用切片进行浅拷贝,然后对副本进行洗牌:

>>> rect = [(0,0),(0,1),(1,1),(1,0)]
>>> sh_rect=rect[:]
>>> random.shuffle(sh_rect)
>>> sh_rect
[(0, 1), (1, 0), (1, 1), (0, 0)]
>>> rect
[(0, 0), (0, 1), (1, 1), (1, 0)]

4
使用random.sample对列表进行洗牌,而不改变原始列表。
from random import sample
rect = [(0,0),(0,1),(1,1),(1,0)]
shuffled_rect = sample(rect, len(rect))

上面的代码片段速度较慢,但是这只是另一种方式。

这种方法是可行的,但在我的测试中,它更快的说法是不正确的。timeit.timeit('random.sample(rect, len(rect))', 'import random; rect = [(0,0),(0,1),(1,1),(1,0)]') 返回了1.77秒,而 timeit.timeit('random.shuffle(rect.copy())', 'import random; rect = [(0,0),(0,1),(1,1),(1,0)]') 返回了1.06秒。我还在简单的range上测试了sampleshufflecopy+shuffle也更快。实际上,当列表很小(10个项目)或很大(1百万个项目)时,sample的时间相对来说真的开始变慢了。 - Chris Pearson

3

您需要复制列表,Python默认情况下只在编写以下代码时创建对同一对象的指针:

disorderd_rectangle = rectangle

但是请使用这种方法或Veky提到的复制方法。
disorderd_rectangle = rectangle[:]

它将复制该列表。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接