洗牌算法——精选推荐
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
洗牌算法
洗牌算法⼀:
⽣成⼀个不重复的随机序列,将随机序列绑定到nums[],然后对随机序列做⼀次排序。
洗牌算法⼆:(经典洗牌算法)
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]所在的位置)
洗牌算法三:(inside-out算法,可⽤于未知牌数)
类似于蓄⽔池抽样算法。
int i=0;
while(nums[i]存在)
{
int k = rand()%(i + 1);
res[i] = res[k];
res[k] = nums[i++];
}
上⾯是伪代码,如果知道nums的lenght的话,可以改为for循环,由于是从前往后遍历,所以可以应对nums[]数⽬未知的情况,或者nums[]是⼀个动态增加的情况。
证明如下:
原数组的第 i 个元素在新数组的前 i 个位置的概率都是:(1/i) * [i/(i+1)] * [(i+1)/(i+2)] *...* [(n-1)/n] = 1/n,(即第i次刚好随机放到了该位置,在后⾯的n-i 次选择中该数字不被选中)
原数组的第 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.。