Shuffle a set of numbers without duplicates.
Example:
// Init an array with set 1, 2, and 3. int[] nums = {1,2,3}; Solution solution = new Solution(nums); // Shuffle the array [1,2,3] and return its result. Any permutation of [1,2,3] must equally likely to be returned. solution.shuffle(); // Resets the array back to its original configuration [1,2,3]. solution.reset(); // Returns the random shuffling of array [1,2,3]. solution.shuffle();我就用的最简单的方法,得到一个随机index,若将该index的数存入随机数组,未出现过,则存入数组,若出现过,则找下一个随机index。没想到AC了,不过时间复杂度bu'za'd
package leetcode;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
public class Shuffle_an_Array_384 {
int[] oriNums;
int n;
Random random=new Random();
public Shuffle_an_Array_384(int[] nums) {
oriNums=nums.clone();
n=nums.length;
}
/** Resets the array to its original configuration and return it. */
public int[] reset() {
return oriNums;
}
/** Returns a random shuffling of the array. */
public int[] shuffle() {
if(n==0){
return new int[]{};
}
int[] result=new int[n];
HashSet<Integer> hashSet=new HashSet<Integer>();
int pointer=0;
while(pointer<n){
int index=random.nextInt(n);
while(hashSet.contains(index)){
index=random.nextInt(n);
}
hashSet.add(index);
result[pointer]=oriNums[index];
pointer++;
}
return result;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] nums=new int[]{1,2,3};
Shuffle_an_Array_384 s=new Shuffle_an_Array_384(nums);
System.out.println(Arrays.toString(s.reset()));
System.out.println(Arrays.toString(s.shuffle()));
}
}
下面看大神的方法:
import java.util.Random;
public class Solution {
private int[] nums;
private Random random;
public Solution(int[] nums) {
this.nums = nums;
random = new Random();
}
/** Resets the array to its original configuration and return it. */
public int[] reset() {
return nums;
}
/** Returns a random shuffling of the array. */
public int[] shuffle() {
if(nums == null) return null;
int[] a = nums.clone();
for(int j = 1; j < a.length; j++) {
int i = random.nextInt(j + 1);
swap(a, i, j);
}
return a;
}
private void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
下面我们来理解一下这个算法的正确性:每个元素有相等的几率出现在数组中的任意一个位置。
对于[1,2,3]这个数组:
循环:j= 1 :0th元素被移到1st位置的概率=( i=0的概率) =1/2
1st元素被移到1st位置的概率=( i=1的概率) =1/2
循环:j=2 :0th元素被移到2nd位置的概率= (已经在1st位置的0th元素与2nd位置交换)+ (保留在0th位置的0th元素与2nd位置交换)
= 1/2*1/3+1/2*1/3
= 1/3
1st元素被移到2nd位置的概率= (已经在0th位置的1st元素与2nd位置交换)+ (保留在1st位置的1st元素与2nd位置交换)
= 1/2*1/3+1/2*1/3
= 1/3
2nd元素被移到2nd位置的概率= 1 - 0th元素被移到2nd位置的概率- 1st元素被移到2nd位置的概率
= 1-1/3-1/3
= 1/3
我们可以给出正确性的小小证明。对于the 0th element,它一直保留在 index 0位置的可能性是 1/1 * (2-1)/2 * (3-1)/3 * ... (n-1)/n = 1/n,它待在index 1位置的可能性是 1/2 * (3-1)/3 * ... = 1/n,它待在index 2位置的可能性是 1/3 * (4-1)/4 * ...=1/n,这意味着对于 the 0th element,它有 1/n 可能性被安置在任意某个位置。
一旦我们知道了0th 元素将会待在 index x。比如,我们知道0th元素将会待在index 0,那么1st 元素待在 1st position 的可能性是多少呢?是 1/1 * 1/2 * ... (n-2)/(n-1) = 1/(n-1). 这说明了对于 1st 元素,它有1/(n-1) 可能性被安置在任何未被占据的位置上。
证明:假设该结论成立,即对于每个position j (starting from 0), 在 [0,j] 范围内的每个数字出现在 position j 的可能性是 1/(1+j)
Let's look at int i = random.nextInt(j + 1):
(1)如果 i == j, nums[j] 就不需要改变它的位置,这种情况的可能性是 1/(1+j).
(2)如果 i !=j, nums[j] 需要与 nums[i] 交换,那么在 [0,j-1] 范围内的某个元素x 位于 position j的可能性=( nums[j]改变了它的位置的可能性) * ( x在position j的可能性)
= (1-1/(1+j)) * (1/j) = 1/(1+j)
则假设成立
对于程序本身,有人疑惑:Why int i = random.nextInt(j + 1);
, not int i = random.nextInt(j);
?
回答是:nextInt(j + 1)
returns a random num between [0, j]
. By nextInt(j)
, you never get a chance to return the original order array.