• 洗牌算法


    洗牌算法一:
    生成一个不重复的随机序列,将随机序列绑定到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.

  • 相关阅读:
    int、bigint、smallint 和 tinyint
    我的fedora9安装后配置
    系统引导设置与管理EasyBCD与VistaBootPRO
    从Xml文档内部引用Xml Schema验证
    linux开机顺序
    OS引导-笔记
    Logic Volume笔记
    Service笔记
    python中若类属性与实例属性同名
    解析JavaScript中的null与undefined
  • 原文地址:https://www.cnblogs.com/tonyluis/p/5944338.html
Copyright © 2020-2023  润新知