洗牌算法一:
生成一个不重复的随机序列,将随机序列绑定到nums[],而后对随机序列作一次排序。java
洗牌算法二:(经典洗牌算法)算法
for(int i=nums.length-1; i>=1; i--) Swap(nums[i], nums[rand()%(i+1)]);
经典算法的证实:数组
对于nums[i],洗牌后在第n-1个位置的几率是1/n(第一次交换的随机数为i)
在n-2个位置几率是[(n-1)/n] * [1/(n-1)] = 1/n,(第一次交换的随机数不为i,第二次为nums[i]所在的位置(注意,若i=n-1,第一交换nums[n-1]会被换到一个随机的位置))
在第n-k个位置的几率是[(n-1)/n] * [(n-2)/(n-1)] *...* [(n-k+1)/(n-k+2)] *[1/(n-k+1)] = 1/n
(第一个随机数不要为i,第二次不为nums[i]所在的位置(随着交换有可能会变)……第n-k次为nums[i]所在的位置)ide
洗牌算法三:(inside-out算法,可用于未知牌数)对象
相似于蓄水池抽样算法。blog
int i=0; while(nums[i]存在) { int k = rand()%(i + 1); res[i] = res[k]; res[k] = nums[i++];
}
上面是伪代码,若是知道nums的lenght的话,能够改成for循环,因为是从前日后遍历,因此能够应对nums[]数目未知的状况,或者nums[]是一个动态增长的状况。排序
证实以下:for循环
原数组的第 i 个元素在新数组的前 i 个位置的几率都是:(1/i) * [i/(i+1)] * [(i+1)/(i+2)] *...* [(n-1)/n] = 1/n,(即第i次恰好随机放到了该位置,在后面的n-i 次选择中该数字不被选中)class
原数组的第 i 个元素在新数组的 i+1 (包括i + 1)之后的位置(假设是第k个位置)的几率是:(1/k) * [k/(k+1)] * [(k+1)/(k+2)] *...* [(n-1)/n] = 1/n(即第k次恰好随机放到了该位置,在后面的n-k次选择中该数字不被选中)随机数
蓄水池抽样:
问题:如何随机从n个对象中选择一个对象,这n个对象是按序排列的,可是在此以前你是不知道n的值的。
解法:咱们老是选择第一个对象,以1/2的几率选择第二个,以1/3的几率选择第三个,以此类推,以1/m的几率选择第m个对象。当该过程结束时,每个对象具备相同的选中几率,即1/n.