5

I am using Collections.shuffle(list); to shuffle a list but I don't know how to unshuffle it?I was thinking of saving the list before shuffling and then shuffle it so that a backup is maintained and can re restored whenever required ,but this seems like a inefficient way of doing it and will take up time and memory ....if you know a more logical way of doing it,can you please elaborate it ?? by the way here is how my app looks :D

enter image description here

enter image description here

Ankit Srivastava
  • 853
  • 1
  • 9
  • 28

6 Answers6

6

There's no such concept of unshuffling - after you've shuffled a deck of cards, how would you get back to the previous state, for example?

If your original collection is ordered in some definitive way, just sort it again. Otherwise (e.g. if it's been hand-ordered) you must take a copy before you shuffle.

In theory you could:

  • Generate a random seed and remember it
  • Create a Random and pass that into shuffle
  • Later, create an ArrayList<Integer> from 0 to size (exclusive)
  • Shuffle that list with a new Random created with the original seed
  • Use the results to work out the original index of each item (because you know where each original item ended up in the shuffled list)

... but that's an awful lot of work. Unless your collection is really too big to keep the extra copy (don't forget it's just a copy of references, not whole objects), I'd just clone the collection before shuffling.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • wow you work at google that's great :D thanks for your answer by the way..I was thinking storing the id's in a list and then regenerating the list again,is that a proper way of dong it?can you suggest a better way? – Ankit Srivastava Jun 14 '14 at 17:03
  • yeah my collection is a list of songs being played ,so that can become big sometimes – Ankit Srivastava Jun 14 '14 at 17:04
  • @AnkitSrivastava: Well if you're going to store the IDs in a list, why not just store the object references instead? Why regenerate the items from the IDs? Are you mutating the objects after shuffling the list? – Jon Skeet Jun 14 '14 at 17:04
  • @AnkitSrivastava: I don't think that's going to count as "big" in any sense I'd normally use the word. Do you often have people with a list of 10,000 songs for example? That's *starting* to get reasonably large (bearing in mind you're on a mobile device). – Jon Skeet Jun 14 '14 at 17:05
  • can you give an example please,of object reference (i am a noob bdw)?a short one will do – Ankit Srivastava Jun 14 '14 at 17:06
  • i know theoretically what references are,and I am aware of pointers in c(know how to use them too ) but please help me out with references in java(a snippet) ,even a small help will do – Ankit Srivastava Jun 14 '14 at 17:08
  • String s = new String("Vijay"); string s2; s=s2; just one question s2 here is a reference and won't take up the same memory (in size) as s will?please tell yes and no and i will be on my way – Ankit Srivastava Jun 14 '14 at 17:12
  • @AnkitSrivastava: No, this is really unrelated to your question - it's a core part of understanding Java. I *strongly* recommend you read a good book about Java development (potentially Android-specific) and pay very, very close attention to sections about variables, references and objects. A Stack Overflow comment thread is not an appropriate place on an unrelated topic is not an appropriate place to start trying to go in to the details of this. (It's not a yes or no question - you've got some fundamental misunderstandings to start with, by the looks of it.) – Jon Skeet Jun 14 '14 at 17:12
  • okay thanks got it...i didn't knew about references in java,thanks a lot :) this linked helped me too http://www.coderanch.com/t/407807/java/java/difference-object-object-reference – Ankit Srivastava Jun 14 '14 at 17:15
4

First, Jon's answer should be used, because that's the simplest and cleanest. Anyway, it is possible to reverse the shuffling with a known random source, since Collections.shuffle() specifies the used algorithm. Again, do not do this, but here's the process for anyone curious:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class Unshuffle {
    public static void main(String[] args) {
        List<Integer> data = new ArrayList<Integer>();
        for (int i = 0; i < 20; i++) {
            data.add(i);
        }
        System.out.println(data);
        Collections.shuffle(data, new Random(42));
        System.out.println(data);
        unshuffle(data, new Random(42));
        System.out.println(data);
    }

    private static <T> void unshuffle(List<T> list, Random rnd) {
        int[] seq = new int[list.size()];
        for (int i = seq.length; i >= 1; i--) {
            seq[i - 1] = rnd.nextInt(i);
        }
        for (int i = 0; i < seq.length; i++) {
            Collections.swap(list, i, seq[i]);
        }
    }
}

And the output is:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[15, 16, 14, 13, 5, 11, 17, 18, 8, 2, 6, 7, 3, 1, 19, 4, 12, 0, 9, 10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
kiheru
  • 6,588
  • 25
  • 31
  • Hi,thanks for your effort,+1 for that ,but you know i don't want to be using the conventional methods,i don't even want to use a new object, – Ankit Srivastava Sep 15 '14 at 10:13
  • @AnkitSrivastava If you're extremely short on memory, `unshuffle()` could avoid creating the temporary random array by using a [reversible PRNG](http://stackoverflow.com/questions/2911432/reversible-pseudo-random-sequence-generator). – kiheru Sep 15 '14 at 11:18
1

use Collections.sort() to sort it back to any order (alphaetical, numerical etc)

bhkiran
  • 389
  • 1
  • 2
  • 11
  • add a field to the playlist object called "int sortorder". and sort the list according to this value. So even if mannualy changed all you need to do is to update the sortorder field. – bhkiran Jun 15 '14 at 16:48
  • yes that is what we are discussing in the answer below – Ankit Srivastava Jun 16 '14 at 09:31
1

We dont unshuffle a list, we just shuffle, it gets randomize The easiest way to do is call the shuffle method from Collections and pass over the arraylist

List<String> myArrayList = new ArrayList<String>;
myArrayList.add("A");
myArrayList.add("B");
myArrayList.add("C");
myArrayList.add("D");
Collections.shuffle(myArrayList);

Iterate the list. it gets shuffled.

Rohit Goyal
  • 550
  • 8
  • 9
0

Add an extra hidden index field to every item in your list. Before shuffling, assign sequential numbers to this hidden field: 1, 2, 3, 4, 5, ... If you want to get the list back into its original order just sort on this hidden field.

You need enough space to store the hidden field and a small amount of processing time.

rossum
  • 15,344
  • 1
  • 24
  • 38
  • yea i was thinking of the same but i guess john's answer is better – Ankit Srivastava Jun 15 '14 at 13:05
  • the reason i didn't go with this method is because the user can delete an item from the list too,so that would mess a lot of things for me ,as i will have to decrease the assigned number for all the items falling after the deleted item – Ankit Srivastava Jun 15 '14 at 13:06
  • A deleted item will not affect the sorting order of the remainder: 1, 2, 4, 5, 6, ... You can always renumber everything before every shuffle, so only the immediately preceding shuffle can be undone. – rossum Jun 15 '14 at 14:14
  • i have 10 items,numbered from 1-10....suppose a user shuffles the list then deletes the number 7,then when he wants to unshuffle,what do you think which item should be placed at 7?and what item should be placed at 10? – Ankit Srivastava Jun 16 '14 at 09:30
  • You do not now have a list of 10 items, but a list of 9 items. Re-ordering on the index numbers will automatically close the gap, moving the old 8 to position 7 etc. and leaving a gap at 10. – rossum Jun 16 '14 at 10:33
  • that's what my point is,re-reordering will have to be done manually,please read my second comment,that's what i said... – Ankit Srivastava Jun 16 '14 at 11:24
0

I know this is old but I took the liberty of implementing* Jon Skeet's theoretical shuffle because it was a cool idea. The PcgRandom algorithm has a feature that allows one to "jump" to arbitrary points within the random number generator and this can be used to "remember" the values that were generated during the shuffle, and reverse it:

var currentIndex = m_currentIndex;
var lastIndex = (m_values.Length - 1);

while (-1 < currentIndex) {
    m_randomNumberGenerator.Jump(-1);

    var randomIndex = m_randomNumberGenerator.NextInt32(currentIndex, lastIndex);
    var tempValue = m_values[randomIndex];

    m_values[randomIndex] = m_values[currentIndex];
    m_values[currentIndex--] = tempValue;

    m_randomNumberGenerator.Jump(-1);
}

Here's the full implementation of the random number generator:

/// <summary>
/// Represents an instance of a fast random number generator; relies on the Pcg32XshRr algorithm described by Melissa O'neil.
/// </summary>
/// <remarks>
/// http://pcg-random.org/paper.html
/// https://en.wikipedia.org/wiki/Lehmer_random_number_generator
/// https://en.wikipedia.org/wiki/Linear_congruential_generator
/// https://github.com/lemire/fastrange
/// </remarks>
public sealed class FastRandom : IRandomNumberGenerator
{
    private const ulong MAX_STREAM_VALUE = ((1UL << 63) - 1UL);

    private readonly ulong m_magic;
    private readonly ulong m_stream;

    private ulong m_state;

    /// <summary>
    /// Gets the default <see cref="FastRandom"/> instance.
    /// </summary>
    public static readonly FastRandom Default = new FastRandom();

    /// <summary>
    /// Initializes a new instance of the <see cref="FastRandom"/> class.
    /// </summary>
    /// <param name="state">The initial state of the random number generator.</param>
    /// <param name="stream">The stream offset of the random number generator.</param>
    [CLSCompliant(false)]
    public FastRandom(ulong state, ulong stream) {
        if (stream > MAX_STREAM_VALUE) {
            throw new ArgumentOutOfRangeException(message: "stream offset must be a positive integer less than 2^63", paramName: nameof(stream));
        }

        m_magic = 6364136223846793005UL;
        m_state = state;
        m_stream = ((((~(stream & 1UL)) << 63) | stream) | 1UL);
    }
    /// <summary>
    /// Initializes a new instance of the <see cref="FastRandom"/> class.
    /// </summary>
    /// <param name="state">The initial state of the random number generator.</param>
    /// <param name="stream">The stream offset of the random number generator.</param>
    public FastRandom(long state, long stream) : this(checked((ulong)state), checked((ulong)stream)) { }
    /// <summary>
    /// Initializes a new instance of the <see cref="FastRandom"/> class.
    /// </summary>
    /// <param name="state">The initial state of the random number generator.</param>
    [CLSCompliant(false)]
    public FastRandom(ulong state) : this(state, SecureRandom.Default.NextUInt64(0UL, MAX_STREAM_VALUE)) { }
    /// <summary>
    /// Initializes a new instance of the <see cref="FastRandom"/> class.
    /// </summary>
    /// <param name="state">The initial state of the random number generator.</param>
    public FastRandom(long state) : this((ulong)state) { }
    /// <summary>
    /// Initializes a new instance of the <see cref="FastRandom"/> class.
    /// </summary>
    public FastRandom() : this(SecureRandom.Default.NextUInt64()) { }

    /// <summary>
    /// Moves the state of the generator forwards by the specificed number of steps.
    /// </summary>
    /// <param name="count">The number of states that will be jumped.</param>
    [CLSCompliant(false)]
    public void Jump(ulong count) {
        Jump(ref m_state, m_magic, m_stream, count);
    }
    /// <summary>
    /// Moves the state of the generator forwards or backwards by the specificed number of steps.
    /// </summary>
    /// <param name="count">The number of states that will be jumped.</param>
    public void Jump(long count) {
        Jump(ref m_state, m_magic, m_stream, unchecked((ulong)count));
    }
    /// <summary>
    /// Generates a uniformly distributed double precision floating point value between the exclusive range (0, 1).
    /// </summary>
    public double NextDouble() {
        return Operations.DoornikDouble(NextInt32(), NextInt32());
    }
    /// <summary>
    /// Generates a uniformly distributed 32-bit signed integer between the range of int.MinValue and int.MaxValue.
    /// </summary>
    public int NextInt32() {
        return ((int)NextUInt32());
    }
    /// <summary>
    /// Generates a uniformly distributed 32-bit signed integer between the inclusive range [x, y].
    /// </summary>
    /// <param name="x">The value of x.</param>
    /// <param name="y">The value of y.</param>
    public int NextInt32(int x, int y) {
        if (x > y) {
            var z = x;

            x = y;
            y = z;
        }

        var range = ((uint)(y - x));

        if (range != uint.MaxValue) {
            return ((int)(Sample(ref m_state, m_magic, m_stream, exclusiveHigh: (range + 1U)) + x));
        }
        else {
            return NextInt32();
        }
    }
    /// <summary>
    /// Generates a uniformly distributed single precision floating point value between the exclusive range (0, 1).
    /// </summary>
    public float NextSingle() {
        return Operations.DoornikSingle(NextInt32());
    }
    /// <summary>
    /// Generates a uniformly distributed 32-bit unsigned integer between the range of uint.MinValue and uint.MaxValue.
    /// </summary>
    [CLSCompliant(false)]
    public uint NextUInt32() {
        return Next(ref m_state, m_magic, m_stream);
    }
    /// <summary>
    /// Generates a uniformly distributed 32-bit unsigned integer between the inclusive range [x, y].
    /// </summary>
    /// <param name="x">The value of x.</param>
    /// <param name="y">The value of y.</param>
    [CLSCompliant(false)]
    public uint NextUInt32(uint x, uint y) {
        if (x > y) {
            var z = x;

            x = y;
            y = z;
        }

        var range = (y - x);

        if (range != uint.MaxValue) {
            return (Sample(ref m_state, m_magic, m_stream, exclusiveHigh: (range + 1U)) + x);
        }
        else {
            return NextUInt32();
        }
    }

    private static void Jump(ref ulong state, ulong magic, ulong stream, ulong count) {
        var accMul = 1UL;
        var accAdd = 0UL;
        var curMul = magic;
        var curAdd = stream;

        while (count > 0UL) {
            if (0UL < (count & 1UL)) {
                accMul *= curMul;
                accAdd = ((accAdd * curMul) + curAdd);
            }

            curAdd = ((curMul + 1UL) * curAdd);
            curMul *= curMul;
            count >>= 1;
        }

        state = ((accMul * state) + accAdd);
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static uint Next(ref ulong state, ulong magic, ulong stream) {
        state = unchecked((state * magic) + stream);

        return Operations.RotateRight(value: ((uint)(((state >> 18) ^ state) >> 27)), count: ((int)(state >> 59)));
    }
    private static uint Sample(ref ulong state, ulong magic, ulong stream, uint exclusiveHigh) {
        ulong sample = Next(ref state, magic, stream);
        ulong result = (sample * exclusiveHigh);
        uint leftover = ((uint)result);

        if (leftover < exclusiveHigh) {
            uint threshold = ((((uint)(-exclusiveHigh)) % exclusiveHigh));

            while (leftover < threshold) {
                sample = Next(ref state, magic, stream);
                result = (sample * exclusiveHigh);
                leftover = ((uint)result);
            }
        }

        return ((uint)(result >> 32));
    }
}

And the full implementation of the shuffle logic:

/// <summary>
/// Represents a strongly typed collection that uses a random number generator to retrieve elements.
/// </summary>
/// <typeparam name="T">The type of elements encapsulated by the bag.</typeparam>
public sealed class ShuffleBag<T> : IEnumerable<T>, IEnumerator<T>
{
    private readonly IRandomNumberGenerator m_randomNumberGenerator;
    private readonly T[] m_values;

    private int m_currentIndex;

    /// <summary>
    /// Gets the element in the collection at the current position of the enumerator.
    /// </summary>
    object IEnumerator.Current => Current;
    /// <summary>
    /// Gets the element in the collection at the current position of the enumerator.
    /// </summary>
    public T Current => m_values[m_currentIndex];

    /// <summary>
    /// Initializes a new instance of the <see cref="ShuffleBag{T}"/> class.
    /// </summary>
    /// <param name="randomNumberGenerator">The source of random numbers that will be used to perform the shuffle.</param>
    /// <param name="values">The array of values that will be randomized.</param>
    public ShuffleBag(IRandomNumberGenerator randomNumberGenerator, T[] values) {
        if (randomNumberGenerator.IsNull()) {
            throw new ArgumentNullException(message: "random number generator cannot be null", paramName: nameof(randomNumberGenerator));
        }

        if (values.IsNull()) {
            throw new ArgumentNullException(message: "array of values cannot be null", paramName: nameof(values));
        }

        m_currentIndex = -1;
        m_randomNumberGenerator = randomNumberGenerator;
        m_values = values;
    }

    /// <summary>
    /// Releases all resources used by this <see cref="ShuffleBag"/> instance.
    /// </summary>
    public void Dispose() { }
    /// <summary>
    /// Returns an enumerator that randomly yields elements from the bag.
    /// </summary>
    IEnumerator IEnumerable.GetEnumerator() {
        return GetEnumerator();
    }
    /// <summary>
    /// Returns an enumerator that randomly yields elements from the bag.
    /// </summary>
    public IEnumerator<T> GetEnumerator() {
        while (MoveNext()) {
            yield return Current;
        }
    }
    /// <summary>
    /// Advances the enumerator to the next random element in the collection.
    /// </summary>
    public bool MoveNext() {
        var currentIndex = m_currentIndex;
        var lastIndex = (m_values.Length - 1);

        if (currentIndex < lastIndex) {
            var randomIndex = m_randomNumberGenerator.NextInt32(++currentIndex, lastIndex);
            var tempValue = m_values[randomIndex];

            m_currentIndex = currentIndex;
            m_values[randomIndex] = m_values[currentIndex];
            m_values[currentIndex] = tempValue;

            return true;
        }
        else {
            return false;
        }
    }
    /// <summary>
    /// Sets the enumerator to its initial position.
    /// </summary>
    /// <param name="unshuffle">Indicates whether elements will be returned to their original, unshuffled, positions.</param>
    public void Reset(bool unshuffle) {
        if (unshuffle) {
            var currentIndex = m_currentIndex;
            var lastIndex = (m_values.Length - 1);

            while (-1 < currentIndex) {
                m_randomNumberGenerator.Jump(-1);

                var randomIndex = m_randomNumberGenerator.NextInt32(currentIndex, lastIndex);
                var tempValue = m_values[randomIndex];

                m_values[randomIndex] = m_values[currentIndex];
                m_values[currentIndex--] = tempValue;

                m_randomNumberGenerator.Jump(-1);
            }
        }

        m_currentIndex = -1;
    }
    /// <summary>
    /// Sets the enumerator to its initial position and returns elements to their original, unshuffled, positions.
    /// </summary>
    public void Reset() {
        Reset(unshuffle: true);
    }
}

*Sorry, it's in C#.

Kittoes0124
  • 4,930
  • 3
  • 26
  • 47