11

I want to shuffle an array, but all I find was method like random.shuffle(x), from Best way to randomize a list of strings in Python

Can I do something like

import random
rectangle = [(0,0),(0,1),(1,1),(1,0)]
# I want something like
# disorderd_rectangle = rectangle.shuffle

Now I can only get away with

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

But it returns

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

So the original array is also changed. How can I just create another shuffled array without changing the original one?

cs95
  • 379,657
  • 97
  • 704
  • 746
ZK Zhao
  • 19,885
  • 47
  • 132
  • 206

5 Answers5

18

People here are advising deepcopy, which is surely an overkill. You probably don't mind the objects in your list being same, you just want to shuffle their order. For that, list provides shallow copying directly.

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

About your misconception: please read http://nedbatchelder.com/text/names.html#no_copies

Veky
  • 2,646
  • 1
  • 21
  • 30
  • 1
    Upvote only because you called a list a list whereas others are still calling it an array – Bhargav Rao May 15 '15 at 06:56
  • LOL. Better for that than for nothing. But seriously, this shows deep misunderstanding of Python's object model. arrays are contiguous in memory, so people will naturally want deepcopy. To us who know how things work, lists are only "skin-deep contiguous", so natural copy is enough. – Veky May 15 '15 at 07:22
4

Use copy.deepcopy to create a copy of the array, shuffle the copy.

c = copy.deepcopy(rectangle)
random.shuffle(c)
  • 1
    So there is nothing like `.shuffle` method? – ZK Zhao May 15 '15 at 06:53
  • [`random.shuffle`](https://docs.python.org/3/library/random.html#random.shuffle) shuffles 'the sequence x in place'. If you want to shuffle a copy, make a copy. Just assigning a new name does not create a copy. –  May 15 '15 at 06:55
4

Use a slice to make a shallow copy, then shuffle the copy:

>>> 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)]
dawg
  • 98,345
  • 23
  • 131
  • 206
4

Use random.sample to shuffle a list without changing the original one.

from random import sample
rect = [(0,0),(0,1),(1,1),(1,0)]
shuffled_rect = sample(rect, len(rect))

The code snippet above is slower but just another way.

  • This method works, though the claim about it being faster was untrue in my testing. `timeit.timeit('random.sample(rect, len(rect))', 'import random; rect = [(0,0),(0,1),(1,1),(1,0)]')` returned 1.77 seconds, while `timeit.timeit('random.shuffle(rect.copy())', 'import random; rect = [(0,0),(0,1),(1,1),(1,0)]')` returned 1.06 seconds. I also tested `sample` vs `shuffle` on a simple `range` and `copy`+`shuffle` was faster there too. In fact the `sample` times really started to blow up comparatively when the list was small (~10 items) or large (~1 million items) – Chris Pearson Aug 31 '21 at 21:49
3

You need to make a copy of the list, by default python only creates pointers to the same object when you write:

disorderd_rectangle = rectangle

But instead use this or the copy method mentioned by Veky.

disorderd_rectangle = rectangle[:]

It will make a copy of the list.