Java 取消混洗复合混洗列表

Java 取消混洗复合混洗列表,java,shuffle,Java,Shuffle,我正在尝试解混洗一个复合混洗列表。我很难想出如何做到这一点 public static void main(String[] args) { // Setup random Random rand = new Random(); rand.setSeed(5); // Setup list ArrayList<Character> list = new ArrayList<Character>(Arrays.asList('v','

我正在尝试解混洗一个复合混洗列表。我很难想出如何做到这一点

public static void main(String[] args) {
    // Setup random
    Random rand = new Random();
    rand.setSeed(5);
    // Setup list
    ArrayList<Character> list = new ArrayList<Character>(Arrays.asList('v','y','2','w','9','n','8','v','a'));
    // Compound shuffle list
    for(int i=0;i<5;i++)
        Collections.shuffle(list, rand);
    // un-shuffle list
    // TODO
}
publicstaticvoidmain(字符串[]args){
//随机设置
Random rand=新的Random();
兰特固定种子(5);
//设置列表
ArrayList=newArrayList(Arrays.asList('v','y','2','w','9','n','8','v','a');
//复合洗牌列表

对于(int i=0;i您需要知道的两件事:

  • 。这项声明特别重要:
  • 如果使用同一种子创建了两个Random实例,则 对每个方法调用的顺序相同,它们将生成 返回相同的数字序列

  • .这一段是关键:
  • 这个实现从上一个向后遍历列表 元素,重复交换随机选择的 元素进入“当前位置”。元素是随机选择的 从列表中从第一个元素到 当前职位(含)

    有了这些知识和一两张图表,您应该能够找出如何扭转这种行为。

    public class Main{
    
    public class Main {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>(Arrays.asList("A", "B", "C", "D", "E", "F", "G"));
            compoundShuffle(list, 8, 13);
            compoundUnshuffle(list, 8, 13);
            System.out.println(list);
        }
    
        public static void compoundShuffle(List<?> list, int repetition, long seed) {
            Random rand = new Random(seed);
            for (int i = 0; i < repetition; i++)
                Collections.shuffle(list, rand);
        }
    
        public static void compoundUnshuffle(List<?> list, int repetition, long seed) {
            helper(list, repetition, seed);
        }
    
        private static <E> void helper(List<E> list, int repetition, long seed) {
            List<Integer> indices = new ArrayList<Integer>();
            int size = list.size();
            for (int i = 0; i < size; i++)
                indices.add(i);
            compoundShuffle(indices, repetition, seed);
            List<E> copy = new ArrayList<E>(list);
            for (int i = 0; i < size; i++)
                list.set(indices.get(i), copy.get(i));
        }
    }
    
    公共静态void main(字符串[]args){ 列表列表=新的ArrayList(Arrays.asList(“A”、“B”、“C”、“D”、“E”、“F”、“G”)); 复合洗牌(列表,8,13); 复合填充物(列表,8,13); 系统输出打印项次(列表); } publicstaticvoidcompoundshuffle(列表、整数重复、长种子){ 随机rand=新随机(种子); for(int i=0;i
    如果在无序化之前知道随机函数的初始种子,那么如果使用相同的种子调用它,则反无序化确实是可能的

    事实上,
    种子
    可以被视为此自定义(因此不是一个好的选择)加密算法的加密密钥

    假设您正在使用Fisher-Yates洗牌算法(我认为Collections.shuffle显然使用了该算法),请尝试以下洗牌和解洗牌算法。该算法适用于int[],将其更改为您的对象[]

    // implementing Fisher–Yates shuffling using a seed
    public static void shuffleArray(int[] ar, int seed) {
        Random r = new Random(seed);
        for (int i = ar.length - 1; i > 0; i--) {
            int index = r.nextInt(i + 1);
            // simple swap
            int a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }   
    
    // implementing Fisher–Yates deShuffler
    //(you should know the **seed** used for shuffling - this is the decryption key)
    public static void deShuffleArray(int[] ar, int seed) {
        //rebuild your random number sequence
        Random r = new Random(seed);
        int[] randoms = new int[ar.length-1];
        int j = 0;
        for (int i = ar.length - 1; i > 0; i--) {
            randoms[j++] = r.nextInt(i + 1);
        }
    
        //deShuffling
        for (int i = 1; i < ar.length; i++) {
            //use the random values backwards
            int index = randoms[ar.length - i - 1];
            // simple swap
            int a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }
    
    //使用种子实现Fisher–Yates洗牌
    公共静态无效随机播放(int[]ar,int seed){
    随机r=新随机(种子);
    对于(int i=ar.length-1;i>0;i--){
    整数指数=r.nextInt(i+1);
    //简单交换
    int a=ar[指数];
    ar[index]=ar[i];
    ar[i]=a;
    }
    }   
    //实施Fisher–Yates Deshufler
    //(您应该知道用于洗牌的**种子**-这是解密密钥)
    公共静态无效解shufflarray(int[]ar,int seed){
    //重建随机数序列
    随机r=新随机(种子);
    int[]randoms=新的int[ar.length-1];
    int j=0;
    对于(int i=ar.length-1;i>0;i--){
    randoms[j++]=r.nextInt(i+1);
    }
    //脱毛
    对于(int i=1;i
    当你说“unshuffle”时,你的意思是“sort”吗?就像你试图让
    list
    恢复其自然顺序一样?是的,我试图让列表恢复其原始顺序。听起来像是家庭作业实际上是我正在进行的加密的一部分。编写自己的加密通常被认为是一个坏主意(tm)。我没有想到创建一个并行整数数组来比较原始列表。这是天才。@TylerBucher这是唯一明智的做法。尝试执行
    n
    集合。如果不使用此技巧,向后洗牌将非常复杂。顺便说一句,谢谢你的提问。我不知道
    Collections.shufflee
    是可预测的。我认为它会使用内部时钟(或其他东西),所以我学到了一些东西。@pbabcdefp删除
    shuffle
    的第二个参数,或者使用
    SecureRandom
    ,但事实并非如此。@ElliottFrisch为此表示感谢。事实上,我以前从未使用过包含两个参数的版本。发现我一直使用的版本还可以,这让我松了一口气。
    // implementing Fisher–Yates shuffling using a seed
    public static void shuffleArray(int[] ar, int seed) {
        Random r = new Random(seed);
        for (int i = ar.length - 1; i > 0; i--) {
            int index = r.nextInt(i + 1);
            // simple swap
            int a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }   
    
    // implementing Fisher–Yates deShuffler
    //(you should know the **seed** used for shuffling - this is the decryption key)
    public static void deShuffleArray(int[] ar, int seed) {
        //rebuild your random number sequence
        Random r = new Random(seed);
        int[] randoms = new int[ar.length-1];
        int j = 0;
        for (int i = ar.length - 1; i > 0; i--) {
            randoms[j++] = r.nextInt(i + 1);
        }
    
        //deShuffling
        for (int i = 1; i < ar.length; i++) {
            //use the random values backwards
            int index = randoms[ar.length - i - 1];
            // simple swap
            int a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }