Java隐藏技-混淆序列

简介: Java隐藏技-混淆序列

题目:假设有一个序列,那么如何根据这个序列生成一个乱序的序列?


看到这个题目,那么首先会想到这样一种解法: 我们将序列的下标进行乱序,再根据这个乱序的下标不就生成一个乱序的序列了吗!


如下是生成序列下标的乱序算法:


int length = list.size();
Set<Integer> set = new LinkedHashSet<>();
Random random = new Random();
while (true) {
  int randomInt = random.nextInt(length);
  set.add(randomInt);
  if (set.size() == length) {
    break;
  }
}

根据这个乱序后的下标的有序集合,就可以生成我们要的乱序序列了。


那么是不是一定要我们自己实现一个这样的算法呢?当然不用了,贴心的J

DK已经提供了这样一个函数,使用起来非常简单,这个函数隐藏在Collections类中


Collections.shuffle(List<?> list);
//和
Collections.shuffle(List<?> list, Random rnd);
  1. 这两个函数都是无返回值得,也就是说是对入参的序列做修改。


  1. 第二个override函数多了一个入参 rnd ,这是一个随机数生成器,可以选用不同的随机数生成算法函数。


如果只是满足使用,嗯~   是不是有点low?


为了高大上一些,看下源码吧!

public static void shuffle(List<?> list) {
        Random rnd = r;
        if (rnd == null)
            r = rnd = new Random(); // harmless race.
        shuffle(list, rnd);
}
public static void shuffle(List<?> list, Random rnd) {
        int size = list.size();
        if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {//SHUFFLE_THRESHOLD = 5
            for (int i=size; i>1; i--)
                swap(list, i-1, rnd.nextInt(i));
        } else {
            Object arr[] = list.toArray();
            // Shuffle array
            for (int i=size; i>1; i--)
                swap(arr, i-1, rnd.nextInt(i));
            // Dump array back into list
            // instead of using a raw type here, it's possible to capture
            // the wildcard but it will require a call to a supplementary
            // private method
            ListIterator it = list.listIterator();
            for (int i=0; i < arr.length; i++) {
                it.next();
                it.set(arr[i]);
            }
        }
    }


JDK提供的混淆算法的关键是这么几句:


// Shuffle array
for (int i=size; i>1; i--)
  swap(arr, i-1, rnd.nextInt(i));

swap函数暂且不看,只要知道是用来交换两项的位置的就可以了,感兴趣的可以下来看源码。

这两句什么含义呢:


  1. 序列使用倒序进行遍历;
  2. nextInt 会取得[0,i)之间的随机数;
  3. 进行两项交换。


现在在大脑中对这个算法进行模拟:


  1. i= size, i-1是序列的最后一项,nextInt 生成了[0,i)之间的一个随机数,并且和序列的最后一项进行了交换;


  1. i=size-1, i-1是序列的倒数第二项,nextInt生成了[0,size-1)之间的随机数,并且和序列的倒数第二项进行了额交换; ...... 当遍历了序列后就得到了一个乱序序列。


程序是看懂了,可为啥这样设计呢?


其实这是对排列组合的一种运用

排列的定义:从n个不同元素中,任取m(m≤n,m与n均为自然数,下同)个元素按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列;从n个不同元素中取出m(m≤n)个元素的所有排列的个数,叫做从n个不同元素中取出m个元素的排列数,用符号 A(n,m)表示。

组合的定义:从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。用符号 C(n,m) 表示。


当然我们这个算法只用到了组合,也就是从n个不同的元素中取出m(m=n)个元素组成一组。


n个元素,假设是从0~9 10个数 那么 0,1,2,3,4,5,6,7,8,9 和 9,8,7,6,5,4,3,2,1,0 仅仅是组合中的两种,那么在其他组合中任取一个组合也可以构成我们需要的混淆序列。


代码表示的就是这种算法,即从下标中取一个和最后一个交换,在在除最后一项外的序列中取一个下标和倒数第二项交换......以此类推,这样得到的一个序列就是一个混淆序列。


相关文章
|
7天前
|
存储 Java 数据处理
|
8月前
|
Java 测试技术
【Java】剑指offer(31)栈的压入、弹出序列
【Java】剑指offer(31)栈的压入、弹出序列
|
8月前
|
Java
编程作业(2) - 编程题 10. DNA序列(Java)
编程作业(2) - 编程题 10. DNA序列(Java)
|
7天前
|
Java
Java栈的压入、弹出序列(详解)
Java栈的压入、弹出序列(详解)
33 0
|
7天前
|
Java Go C++
Golang每日一练(leetDay0112) 2、3、4的幂
Golang每日一练(leetDay0112) 2、3、4的幂
32 0
Golang每日一练(leetDay0112) 2、3、4的幂
|
7天前
|
Java Go C++
Golang每日一练(leetDay0085) 2的幂、数字 1 的个数
Golang每日一练(leetDay0085) 2的幂、数字 1 的个数
27 0
Golang每日一练(leetDay0085) 2的幂、数字 1 的个数
|
7天前
|
Java Go C++
Java每日一练(20230420) 罗马数字转整数、电话号码的字母组合、排列序列
Java每日一练(20230420) 罗马数字转整数、电话号码的字母组合、排列序列
26 0
Java每日一练(20230420) 罗马数字转整数、电话号码的字母组合、排列序列
|
7天前
|
Java
105. 从前序与中序遍历序列构造二叉树 --力扣 --JAVA
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
30 1
|
8月前
|
Java
【Java每日一题,动态规划,Map实现】最长定差子序列
【Java每日一题,动态规划,Map实现】最长定差子序列
|
8月前
|
Java
【Java每日一题,动态规划】最长定差子序列
【Java每日一题,动态规划】最长定差子序列