• 随机洗牌


    问题:给定一个有序序列1~n,要你将其完全打乱,要求每个元素在任何一个位置出现的概率均为1/n。

    解决方案:依次遍历数组,对第n个元素,以1/n的概率与前n个元素中的某个元素互换位置,最后生成的序列即满足要求,1/n的概率可通过rand() % n实现。见如下程序:

    void swap(int* p, int* q)

    {

        int tmp = *p;

        *p = *q;

        *q = tmp;

    }

     

    void shuffle(int *arr, int n)

    {

        int i;

        for(i = 0; i < n; i++) {

            int idx = rand() % (i + 1); //选取互换的位置

            swap(&arr[idx], &arr[i]);

        }

    }

     

     使用数学归纳法证明:

    l  当n=1时,idx必为0,所以元素arr[0]在任何一个位置的概率为1/1,命题成立。

    l  假设当n=k时,命题成立,即n=k时,原数组中任何一个元素在任何一个位置的概率为1/k。

     则当n=k+1时,当算法执行完k次时,前k个元素在前k个位置的概率均为1/k。

     当执行最后一步时,前k个元素中任何一个元素被替换到第k+1位置的概率为:(1-1/(k+1)) * 1/k = 1/(k+1); 在前面k个位置任何一个位置的概率为(1-1/(k+1)) * 1/k = 1/(k+1);

    故前k个元素在任意位置的的概率都为1/(k+1)

     所以,对于前k个元素,它们在k+1的位置上概率为1/(k+1)。

     对于第k+1个元素,其在原位置的概率为1/k+1,在前k个位置任何一个位置的概率为:(1-k/(k+1)) * (1/k)=1/(k+1),所以对于第k+1个元素,其在整个数组前k+1个位置上的概率也均为1/k+1。

     综上所述,对于任意n,只要按照方案中的方法,即可满足每个元素在任何一个位置出现的概率均为1/n。

     

    扩展:一道google面试题

    给定一个未知长度的整数流(数目大于1000),如何从中随机选取1000个随机数。

     

    解决方法:

    l  定义长度为1000的数组,对于数据流中的前1000个关键字,显然都要放到数组中。

    l  对于数据流中的的第n(n>1000)个关键字,则这个关键字被随机选中的概率为 1000/n。故以 1000/n 的概率用这个关键字去替换数组中的一个。这样就可以保证所有关键字都以 1000/n的概率被选中。对于后面的关键字都进行这样的处理,这样就可以保证数组中总是保存着1000个随机关键字。

     

    注:以1000/n的概率选择一个数替换,可通过rand() % n实现,则这个数被替换到前1000个位置中的概率为1000/n。

    转自:http://blog.chinaunix.net/uid-20196318-id-216658.html

  • 相关阅读:
    git 合并多个commit
    git 修改 Commit Message
    git rebase 命令介绍
    git 忘记切换分支,误将代码commit到了别的分支的解决方法
    会话层的会话和传输层中的连接的区别
    Goland 安装 k8s 源码 的步骤
    Linux export 命令的作用
    Linux 执行脚本时 source 和 . 和 sh 和 ./ 的区别
    the connection to the server 6443 was refused
    Kubernetes 创建 Pod 时,背后到底发生了什么?
  • 原文地址:https://www.cnblogs.com/freeopen/p/5482894.html
Copyright © 2020-2023  润新知