Java中 shuffle 算法的使用

Fisher–Yates shuffle 基本思想(Knuth shuffle ):

To shuffle an array a of n elements (indices 0..n-1):
  for i from n − 1 downto 1 do
       j ← random integer with 0 ≤ j ≤ i
       exchange a[j] and a[i]

JDK源代码如下:


代码如下:

/**
     * Moves every element of the List to a random new position in the list.
     *
     * @param list
     *            the List to shuffle
     *
     * @throws UnsupportedOperationException
     *             when replacing an element in the List is not supported
     */
    public static void shuffle(List<?> list) {
        shuffle(list, new Random());
    }

/**
     * Moves every element of the List to a random new position in the list
     * using the specified random number generator.
     *
     * @param list
     *            the List to shuffle
     * @param random
     *            the random number generator
     *
     * @throws UnsupportedOperationException
     *             when replacing an element in the List is not supported
     */
    @SuppressWarnings("unchecked")
    public static void shuffle(List<?> list, Random random) {
        if (!(list instanceof RandomAccess)) {
            Object[] array = list.toArray();
            for (int i = array.length - 1; i > 0; i--) {
                int index = random.nextInt(i + 1);
                if (index < 0) {
                    index = -index;
                }
                Object temp = array[i];
                array[i] = array[index];
                array[index] = temp;
            }

int i = 0;
            ListIterator<Object> it = (ListIterator<Object>) list
                    .listIterator();
            while (it.hasNext()) {
                it.next();
                it.set(array[i++]);
            }
        } else {
            List<Object> rawList = (List<Object>) list;
            for (int i = rawList.size() - 1; i > 0; i--) {
                int index = random.nextInt(i + 1);
                if (index < 0) {
                    index = -index;
                }
                rawList.set(index, rawList.set(i, rawList.get(index)));
            }
        }
    }

测试代码,为了确保每种情况的初始化一样,使用了多个容器:


代码如下:

public class javaShuffle {
    public static int temp = 0;
    public static long start;
    public static long end;

public static void main(final String args[]) {

Object changeTemp;
        List<Integer> numList = new ArrayList<Integer>();
        List<Integer> firstList = new ArrayList<Integer>();
        List<Integer> secondList = new ArrayList<Integer>();
        List<Integer> thirdList = new ArrayList<Integer>();
        List<Integer> fourthList = new ArrayList<Integer>();

for (int i = 1; i <= 100000; i++) {
            numList.add(i);
            firstList.add(i);
            secondList.add(i);
            thirdList.add(i);
            fourthList.add(i);
        }

// first shuffle,use changeTemp
        getStartTime();
        int randInt = 0;
        for (int i = 0, length = firstList.size(); i < length; i++) {
            randInt = getRandom(i, firstList.size());
            changeTemp = firstList.get(i);
            firstList.set(i, firstList.get(randInt));
            firstList.set(randInt, javaShuffle.temp);
        }
        getEndTime("first shuffle run time ");

// second shuffle,exchange list
        getStartTime();
        for (int i = 0, length = secondList.size(); i < length; i++) {
            randInt = getRandom(i, secondList.size());
            secondList.set(i, secondList.set(randInt, secondList.get(i)));
        }
        getEndTime("second shuffle run time");

// third shuffle, change generate random int
        getStartTime();
        Object[] tempArray = thirdList.toArray();
        Random rand = new Random();
        int j = 0;
        for (int i = tempArray.length - 1; i > 0; i--) {
            j = rand.nextInt(i + 1);
            thirdList.set(i, thirdList.set(j, thirdList.get(i)));
        }

getEndTime("third shuffle run time ");

// fourth shuffle, simulate java shuffle
        getStartTime();
        Random random = new Random();
        if (!(fourthList instanceof RandomAccess)) {
            Object[] array = fourthList.toArray();
            for (int i = array.length - 1; i > 0; i--) {
                int index = random.nextInt(i + 1);
                if (index < 0) {
                    index = -index;
                }
                Object temp = array[i];
                array[i] = array[index];
                array[index] = temp;
            }

int i = 0;
            ListIterator<Integer> it = (ListIterator<Integer>) fourthList.listIterator();
            while (it.hasNext()) {
                it.next();
                it.set((Integer) array[i++]);
            }
        } else {
            List<Integer> rawList = (List<Integer>) fourthList;
            for (int i = rawList.size() - 1; i > 0; i--) {
                int index = random.nextInt(i + 1);
                if (index < 0) {
                    index = -index;
                }
                rawList.set(index, rawList.set(i, rawList.get(index)));
            }
        }
        getEndTime("fourth shuffle run time");

// java shuffle
        getStartTime();
        Collections.shuffle(numList);
        getEndTime("java shuffle run time  ");
    }

public static void swap(int a, int b) {
        javaShuffle.temp = a;
        a = b;
        b = javaShuffle.temp;
    }

public static int getRandom(final int low, final int high) {
        return (int) (Math.random() * (high - low) + low);
    }

public static void getStartTime() {
        javaShuffle.start = System.nanoTime();
    }

public static void getEndTime(final String s) {
        javaShuffle.end = System.nanoTime();
        System.out.println(s + ": " + (javaShuffle.end - javaShuffle.start) + "ns");
    }

}

如果数值较小,例如100000级别,则输出大概是:

first shuffle run time : 85029499ns
second shuffle run time: 80909474ns
third shuffle run time : 71543926ns
fourth shuffle run time: 76520595ns
java shuffle run time  : 61027643ns

first shuffle run time : 82326239ns
second shuffle run time: 78575611ns
third shuffle run time : 95009632ns
fourth shuffle run time: 105946897ns
java shuffle run time  : 90849302ns

first shuffle run time : 84539840ns
second shuffle run time: 85965575ns
third shuffle run time : 101814998ns
fourth shuffle run time: 113309672ns
java shuffle run time  : 35089693ns

first shuffle run time : 87679863ns
second shuffle run time: 79991814ns
third shuffle run time : 73720515ns
fourth shuffle run time: 78353061ns
java shuffle run time  : 64146465ns

first shuffle run time : 84314386ns
second shuffle run time: 80074803ns
third shuffle run time : 74001283ns
fourth shuffle run time: 79931321ns
java shuffle run time  : 86427540ns

first shuffle run time : 84315523ns
second shuffle run time: 81468386ns
third shuffle run time : 75052284ns
fourth shuffle run time: 79461407ns
java shuffle run time  : 66607729ns

多次运行结果可能都不一样,但是基本java自带 shuffle速度最快,第三种方式次之。而第一种方法耗时最久。

如果是10000000级别,大概如下:

first shuffle run time : 2115703288ns
second shuffle run time: 3114045871ns
third shuffle run time : 4664426798ns
fourth shuffle run time: 2962686695ns
java shuffle run time  : 3246883026ns first shuffle run time : 2165398466ns
second shuffle run time: 3129558913ns
third shuffle run time : 4147859664ns
fourth shuffle run time: 2911849942ns
java shuffle run time  : 4311703487ns first shuffle run time : 2227462247ns
second shuffle run time: 3279548770ns
third shuffle run time : 4704344954ns
fourth shuffle run time: 2942635980ns
java shuffle run time  : 3933172427ns first shuffle run time : 2200158789ns
second shuffle run time: 3172666791ns
third shuffle run time : 4715631517ns
fourth shuffle run time: 2950817535ns
java shuffle run time  : 3387417676ns first shuffle run time : 2201124449ns
second shuffle run time: 3203823874ns
third shuffle run time : 4179926278ns
fourth shuffle run time: 2913690411ns
java shuffle run time  : 3571313813ns first shuffle run time : 2163053190ns
second shuffle run time: 3073889926ns
third shuffle run time : 4493831518ns
fourth shuffle run time: 2852713887ns
java shuffle run time  : 3773602415ns

可以看出,第一种方法速度最快,而第四种最慢。java自带 shuffle速度也不理想。

在进行大数据处理的时候,如果使用java库效率较低时,可以考虑使用其他方式。

(0)

相关推荐

  • Java中 shuffle 算法的使用

    Fisher–Yates shuffle 基本思想(Knuth shuffle ): To shuffle an array a of n elements (indices 0..n-1):  for i from n − 1 downto 1 do       j ← random integer with 0 ≤ j ≤ i       exchange a[j] and a[i] JDK源代码如下: 复制代码 代码如下: /**     * Moves every element of

  • java 中归并排序算法详解

    java 中归并排序算法详解 归并排序算法,顾名思义,是一种先分再合的算法,其算法思想是将要排序的数组分解为单个的元素,每个元素就是一个单个的个体,然后将相邻的两个元素进行从小到大或从大到小的顺序排序组成一个整体,每个整体包含一到两个元素,然后对相邻的整体继续"合"并,因为每个整体都是排过序的,因而可以采用一定的算法对其进行合并,合并之后每个整体包含三到四个元素,继续对相邻的整体进行合并,直到所有的整体都合并为一个整体,最终得到的整体就是将原数组进行排序之后的结果. 对于相邻的整体,其

  • java 中基本算法之希尔排序的实例详解

    java 中基本算法之希尔排序的实例详解 希尔排序(Shell Sort)是插入排序的一种.也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本.希尔排序是非稳定排序算法.该方法因DL.Shell于1959年提出而得名. 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序:随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止. 基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差

  • 详解Java中KMP算法的图解与实现

    目录 图解 代码实现 图解 kmp算法跟之前讲的bm算法思想有一定的相似性.之前提到过,bm算法中有个好后缀的概念,而在kmp中有个好前缀的概念,什么是好前缀,我们先来看下面这个例子. 观察上面这个例子,已经匹配的abcde称为好前缀,a与之后的bcde都不匹配,所以没有必要再比一次,直接滑动到e之后即可. 那如果好前缀中有互相匹配的字符呢? 观察上面这个例子,这个时候如果我们直接滑到好前缀之后,则会过度滑动,错失匹配子串.那我们如何根据好前缀来进行合理滑动? 其实就是看当前的好前缀的前缀和后缀

  • Java中Prime算法的原理与实现详解

    目录 Prim算法介绍 1.点睛 2.算法介绍 3. 算法步骤 4.图解 Prime 算法实现 1.构建后的图 2.代码 3.测试 Prim算法介绍 1.点睛 在生成树的过程中,把已经在生成树中的节点看作一个集合,把剩下的节点看作另外一个集合,从连接两个集合的边中选择一条权值最小的边即可. 2.算法介绍 首先任选一个节点,例如节点1,把它放在集合 U 中,U={1},那么剩下的节点为 V-U={2,3,4,5,6,7},集合 V 是图的所有节点集合. 现在只需要看看连接两个集合(U 和 V-U)

  • 图解Java中插入排序算法的原理与实现

    目录 一.基本思想 二.算法分析 1.算法描述 2.过程分析 三.算法实现 一.基本思想 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入. 二.算法分析 1.算法描述 一般来说,插入排序都采用in-place在数组上实现.具体算法描述如下: 从第一个元素开始,该元素可以认为已经被排序: 取出下一个元素,在已经排序的元素序列中从后向前扫描: 如果该元素(已排序)大于新元素,将

  • 图解Java中归并排序算法的原理与实现

    目录 一.基本思想 二.算法分析 1.算法描述 2.过程分析 3.动图演示 三.算法实现 一.基本思想 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列:即先使每个子序列有序,再使子序列段间有序.若将两个有序表合并成一个有序表,称为2-路归并. 二.算法分析 1.算法描述 把长度为n的输入序列分成两个长度为n/2的子序列:对这两个子序列分别采用归并排序:将两个排序好的子序列合并

  • java中gc算法实例用法

    在我们对gc中的算法有基本概念理解后,要把算法的理念实现还需要依托实际垃圾收集器的使用.因为光靠一些简单的原理不足以支撑整个程序的运行,在回收机制上有专门的收集器.下面我们就垃圾收集器的概念.使用注意事项.收集器图解进行介绍,然后带来两种常见的垃圾收集器供大家参考. 1.概念 垃圾收集器时之前列举的垃圾收集算法的具体实现. 2.注意事项 每一个回收器都存在Stop The World 的问题,只不过各个回收器在Stop The World 时间优化程度.算法的不同,可根据自身需求选择适合的回收器

  • 详解Java中AC自动机的原理与实现

    目录 简介 工作过程 数据结构 初始化 构建字典树 构建失败指针 匹配 执行结果 简介 AC自动机是一个多模式匹配算法,在模式匹配领域被广泛应用,举一个经典的例子,违禁词查找并替换为***.AC自动机其实是Trie树和KMP 算法的结合,首先将多模式串建立一个Tire树,然后结合KMP算法前缀与后缀匹配可以减少不必要比较的思想达到高效找到字符串中出现的匹配串. 如果不知道什么是Tire树,可以先查看:详解Java中字典树(Trie树)的图解与实现 如果不知道KMP算法,可以先查看:详解Java中

  • Java实现插入排序算法可视化的示例代码

    参考文章 图解Java中插入排序算法的原理与实现 实现效果 示例代码 import java.awt.*; public class AlgoVisualizer { private static int DELAY = 40; private InsertionSortData data; private AlgoFrame frame; public AlgoVisualizer(int sceneWidth, int sceneHeight, int N){ // 初始化数据 data =

随机推荐