洗牌算法就是给你一个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
浙公网安备 33010602011771号