6

I would like to random shuffle a list so that each variable in the list when shuffled gets put in a new place in the list.

What I am currently doing:

list = ['a', 'b','c', 'd'];
random.shuffle(list)

list
['c','b','d','a']

With this method I shuffle the list but it is still possible to have a variable end up in the same place in this case 'b'.

My desired output

completely shuffled list

['c','a','d','b']

I appreciate any help. I am new to python but please let me know if any further information is needed.

Dre
  • 713
  • 1
  • 8
  • 27
  • 3
    how big is your list expected to be? roughly-speaking. – matias elgart Dec 06 '16 at 23:26
  • @matiaselgart my list will include 15 variables. – Dre Dec 06 '16 at 23:28
  • 4
    Just a note: If you want each element at a truly random position, rejecting arrangements that don't move every element actually reduces the "randomness". It's _extremely_ rare you'd have a situation where it's more correct/secure to arbitrarily reject any given random arrangement; not allowing an element to maintain the same position reveals _more_ information than letting them be shuffled completely randomly. – ShadowRanger Dec 07 '16 at 00:02
  • 2
    @ShadowRanger Good point. It isn't exactly the same, but a key weakness of the German enigma machine was that a letter in a given position never encrypted to itself. – John Coleman Dec 07 '16 at 00:04

1 Answers1

5

Something like this should do what you want:

import random
import copy

def super_shuffle(lst):
    new_lst = copy.copy(lst)
    random.shuffle(new_lst)
    for old, new in zip(lst, new_lst):
        if old == new:
            return super_shuffle(lst)

    return new_lst

Example:

In [16]: super_shuffle(['a', 'b', 'c'])
Out[16]: ['b', 'c', 'a']
wpercy
  • 9,636
  • 4
  • 33
  • 45
Jack
  • 20,735
  • 11
  • 48
  • 48
  • i ran this code with an input list of ['a', 'b', 'c', 'd', 'e', 'f'] multiple times, sometimes getting a new list back with elements in the same position. – matias elgart Dec 06 '16 at 23:39
  • 1
    This is probably the best approach. On average roughly 1/e (around 37%) of permutations are derangements, almost independently of the list size, so you never have to go through too many trials before you get a hit. – John Coleman Dec 06 '16 at 23:40
  • @John Coleman Thanks. Updated. – Jack Dec 06 '16 at 23:46
  • @matiaselgart I don't think there is a bug. If you "super shuffle" the super-shuffled list then you might get something which is in the same place as the original list, but that is irrelevant, the original code worked as intended. Derangements are not closed under composition. – John Coleman Dec 06 '16 at 23:49
  • 1
    @Jack `zip` is more elegant than indices, but your original code worked. – John Coleman Dec 06 '16 at 23:49
  • @JohnColeman yup, you're right of course. user error. – matias elgart Dec 06 '16 at 23:56
  • @Jack You might want to add this link to your answer: https://en.wikipedia.org/wiki/Derangement . It gives the 1/e result I mentioned. Your code is fairly fast, working on a list of 10,000 elements almost instantly and a list of 1,000,000 in about 2 seconds. – John Coleman Dec 06 '16 at 23:59