8

I need to shuffle an array so that all array elements should change their location. Given an array [0,1,2,3] it would be ok to get [1,0,3,2] or [3,2,0,1] but not [3,1,2,0] (because 2 left unchanged). I suppose algorithm would not be language-specific, but just in case, I need it in C++ program (and I cannot use std::random_shuffle due to the additional requirement).

mechanical_meat
  • 163,903
  • 24
  • 228
  • 223
Nick
  • 3,205
  • 9
  • 57
  • 108
  • Do you have any memory requirements? You could make a new array the same size as the first, and then just move each element in array1 into a new, random position in array2. If the value has already been set, try again. Very brute force. – Liron Feb 26 '13 at 18:09
  • Another approach: Start with index 0, and swap it with a random other index. Keep a list of all indices that have already been swapped. Iterate over the list, only swapping with unswapped items. – Liron Feb 26 '13 at 18:11
  • 2
    Simplest approach, though completely not random - shift all of the array elements by x positions. Should be easy to do in any language, just create a new array, copy the first bunch in at an offset, then copy the rest in at the beginning. – Scott Mermelstein Feb 26 '13 at 18:13
  • 1
    Why can't you use `std::random_shuffle`? Do answers have to avoid all of ``? All of the C++ library? All libraries? – Mooing Duck Feb 26 '13 at 18:14
  • Do you require it to be random? You could just move the last value into the first position, and move all the rest by one. – Chad Feb 26 '13 at 18:14
  • 1
    It sounds like you're looking for a random derangement. See http://www.dsi.unifi.it/~merlini/papers/Derangements.pdf for an algorithm. – Mark Dickinson Feb 26 '13 at 19:03
  • Possible duplicate of [Shuffle list, ensuring that no item remains in same position](https://stackoverflow.com/questions/7279895/shuffle-list-ensuring-that-no-item-remains-in-same-position) – BeeOnRope May 19 '18 at 06:48

4 Answers4

8

What about this?

  1. Allocate an array which contains numbers from 0 to arrayLength-1
  2. Shuffle the array
  3. If there is no element in array whose index equals its value, continue to step 4; otherwise repeat from step 2.
  4. Use shuffled array values as indexes for your array.
anatolyg
  • 26,506
  • 9
  • 60
  • 134
Boris Šuška
  • 1,796
  • 20
  • 32
4
For each element e
    If there is an element to the left of e
        Select a random element r to the left of e
           swap r and e

This guarantees that each value isn't in the position that it started, but doesn't guarantee that each value changes if there's duplicates.

BeeOnRope notes that though simple, this is flawed. Given the list [0,1,2,3], this algorithm cannot produce the output [1,0,3,2].

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • I believe that's a variant of Fisher-Yates shuffle, known as [Sattolo's algorithm](http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle). – jrok Feb 26 '13 at 21:27
  • It's a pretty simple algorithm, I'd be surprised if nobody else had thought of it. I'd heard of Fisher-Yates, but not Sattolo before. – Mooing Duck Feb 26 '13 at 21:53
  • 2
    This fails to generate all the possible _derangements_. Consider the list `[0,1,2,3]` and the first allowed output above: `[1,0,3,2]`. This output can never be generated! Note that at the last step (about to swap the 3) the list looks like `[?,?,X,3]` where `X` cannot be 2 since it was just swapped into one of the `?` positions in the previous step. So the only way for `X` to end up with 3 after the last step means that the list looks like `[?,?,3,X]` and X cannot be 2, so the arrangement cannot be generated. This algorithm only generates a small fraction of the possible derangements. – BeeOnRope May 19 '18 at 06:43
  • @BeeOnRope: In 5 years, nobody noticed that. Well done! Fixed. – Mooing Duck May 20 '18 at 22:59
  • 1
    The new algorithm is simply a (Fisher-Yates) shuffle - elements can end up in the same place they start, violating the OPs requirement. AFAIK, there is no simple direct derangement generating algorithm that I'm aware of - papers are written about the ones that exist. A reasonable probabilistic approach is just to generate a shuffle as above, then check if any element stayed in place, and retry if so. – BeeOnRope May 20 '18 at 23:04
2

It's not going to be very random, but you can rotate all the elements at least one position:

std::rotate(v.begin(), v.begin() + (rand() % v.size() - 1) + 1, v.end());

If v was {1,2,3,4,5,6,7,8,9} at the beginning, then after rotation it will be, for example: {2,3,4,5,6,7,8,9,1}, or {3,4,5,6,7,8,9,1,2}, etc.

All elements of the array will change position.

piokuc
  • 25,594
  • 11
  • 72
  • 102
  • 1
    Is there a problem with this solution? – piokuc Feb 26 '13 at 18:20
  • 1
    This doesn't shuffle the array. It gives a predictable result. – David G Feb 26 '13 at 18:22
  • +1. Simple and correct solution. I don't see any reason to include randomness in the solution, it has nothing to do with the stated problem. – SomeWittyUsername Feb 26 '13 at 18:23
  • Even if the result is predictable, one of the acceptable "solutions" in the original question indicates that this would be OK. `[0,1,2,3]` can become `[3,1,2,0]`. – Chad Feb 26 '13 at 18:24
  • It's changing position of every element. Where did you get your definition of shuffle as an operation which does not give predictable results? – piokuc Feb 26 '13 at 18:26
  • How about this now. Can you predict the result? – piokuc Feb 26 '13 at 18:28
1

I kind of have a idea in my mind hope it fits your application. Have one more container and this container will be a "map(int,vector(int))" . The key element will show index and the second element the vector will hold the already used values.

For example for the first element you will use rand function to find which element of the array you should use.Than you will check the map structure if this element of the array has been used for this index.

Kadir Erdem Demir
  • 3,531
  • 3
  • 28
  • 39