2

If I shuffle a small list using python's random.shuffle, I'm getting also unshuffled results.

import random

for i in range(10):
    ori = [1, 2, 3]
    per = ori[:]
    random.shuffle(per)
    print i, per, (per == ori) or ""

Here is a sample output:

0 [1, 3, 2] 
1 [1, 2, 3] True
2 [3, 1, 2] 
3 [2, 3, 1] 
4 [1, 2, 3] True
5 [2, 3, 1] 
6 [3, 2, 1] 
7 [2, 1, 3] 
8 [2, 1, 3] 
9 [2, 1, 3] 

I understand that this must necessarily be the case, after looking into the algorithm details [1], [2]. But I really want get a small unsorted list (say 3 to 6 items) programmatically.

What do you think is the best approach to do this?

Community
  • 1
  • 1
Wolf
  • 9,679
  • 7
  • 62
  • 108

3 Answers3

4

If you must exclude the original ordering, you could simply retry the shuffle until per != ori.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • I thought so, this makes for cases of [1, 2] always [2, 1] ;) - Is this the only way? I would not ask if there were not a large amount of examples with only 4 items around... ...and if a functions has the name *shuffle* what would you expect it to do? – Wolf Sep 07 '14 at 11:10
  • Since you're asking, what the function does is perfectly in line with what *I* would expect it to do. – NPE Sep 07 '14 at 11:14
  • Yes, the mathematical POV doesn't match the pragmatic one. – Wolf Sep 07 '14 at 11:23
  • 1
    I guess that depends on the exact use case. For example, if I take 2, 3 and 4 of hearts and shuffle them by hand, it would not surprise me if they would occasionally come out in the original order. `random.shuffle()` is basically the same deal (pardon the pun). – NPE Sep 07 '14 at 11:33
  • 2
    @Wolf: I think it matches the pragmatic one too. If I handed you 3 cards and asked you to shuffle them, I would expect that the original order had an equal chance to be the result as any other ordering. – Brian Sep 07 '14 at 11:34
  • True, seems I indeed was looking for the `unsort` function here. I should really play cards more often ;) – Wolf Sep 07 '14 at 11:36
1

Well one way would be generate permutations of the list and then drop the first item. After that you can use random.choice to pick any of the permutation:

>>> from random import choice
>>> from itertools import permutations
>>> data = list(permutations([1, 2, 3], 3))[1:]
>>> for _ in range(10):
...     print choice(data)
...     
(3, 2, 1)
(3, 2, 1)
(2, 1, 3)
(1, 3, 2)
(1, 3, 2)
(1, 3, 2)
(3, 2, 1)
(2, 3, 1)
(2, 3, 1)
(2, 1, 3)
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • Ok "reimplementing" `random.shuffle` seems to make sense in my case. – Wolf Sep 07 '14 at 11:16
  • I accept your answer, because it's more pragmatic. NPE rephrased my question, which is often a good idea, also in this case, and the discussion helped a lot for understanding. So I don't apply either of them this time, I'm still reflecting *my actual goals*. Anyway, I find this answer matches my original question best - as I understand it now ;) – Wolf Sep 12 '14 at 14:48
-1

Since it's a short array do a quick shuffle manually:

import random

for i in range(len(ori) - 1):
    j = random.randint(i + 1, len(ori) - 1)
    ori[i], ori[j] = ori[j], ori[i]

This way you will ensure that you won't get the original array. This is an O(n) solution, you should only use it on small arrays.

mpcabd
  • 1,813
  • 15
  • 20
  • Did you try this? There's no [1, 3, 2]. – Wolf Sep 07 '14 at 11:26
  • Sorry I thought you only wanted to shuffle the array and make sure you don't get the original array. If you want to get all the possible permutations of the array you should go with [Ashwini Chaudhary's answer](http://stackoverflow.com/a/25709572/99557) – mpcabd Sep 07 '14 at 11:37