How to unshuffle a list?

只谈情不闲聊 提交于 2020-01-15 07:15:27

问题


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


回答1:


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.




回答2:


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]



回答3:


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




回答4:


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.




回答5:


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.




回答6:


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#.



来源:https://stackoverflow.com/questions/24222204/how-to-unshuffle-a-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!