洗牌算法就是给你一个1到n的序列,让你随机打乱,保证每个数出现在任意位置的概率相同,也就是n!个的排列中,每一个排列出现的概率相同。

      最常用的方法,每次随机选出一个没有被选过的数放到一个队列中,如果随机出来的数已经被选过,那么继续随机直到遇到一个没有被选过的数放入到队列中;重复这样的操作直到所有的数都被选择出来。

      这种方法,第一个数选择有n个数可选,第2个数选择有(n-1)个数可选,其他的数选择类推,这样,最后选择出来的排列是n!的排列中的任意一个,显然是符合随机洗牌的要求的。由于这种方法每次随机选择一个数都有可能是之前的选过的数,需要再次随机。因此对选出一个数的随机平均情况下需要随机O(n)次,因此该方法的时间复杂度是O(n^2)。

      最有效的方法是,当一个数被选之后,没有必要再下一次随机的时候再考虑它的,因此,每次只从可选的数的集合中进行随机,也就不用考虑是否会碰到已经选过的数了。这种算法的时间复杂度为O(n)。

//方法一
public int[] shuffle(int length){
        if(length<=0){
               return new int[0];
        }
        int[] res=new int[length];
        Random r=new Random();
        int idx;
        //在赋值的同时进行随机交换
        for(int i=0;i<length;i++){
               idx=r.nextInt(i+1);
               res[i]=res[idx];
               res[idx]=i+1;
        }
        return res;
}

//方法二
public int[] shuffle(int length){
       if(length<=0){
              return new int[0];
       }
       List<Integer> list=new ArrayList<Integer>();
       for(int i=0;i<length;i++){
              list.add(i+1);
       }
       Collections.shuffle(list);
       //将List集合转换为Array数组
       Integer[] intArr=(Integer)list.toArray(new Integer[length]);
       //Interger[]转换为int[]
       int[] ints=Arrays.stream(intArr).mapToInt(Integer::valueOf).toArray();
       return ints;
}

//方法三
public int[] shuffle(int length){
       if(length<=0){
               return new int[0];
       }
       List<Integer> list=new ArrayList<>();
       for(int i=1;i<=length;i++){
               list.add(i);
       }
       int[] ints=new int[length];
       for(int i=0;i<length;i++){
             ints[i]=list.remove(new Random().nextInt(length-i));
       }
       return ints;
}

 

 posted on 2018-07-08 21:33  会飞的金鱼  阅读(229)  评论(0)    收藏  举报