根据离散离散概率分布抽样是一个常见的问题。这篇文章将介绍运行时间复杂度为O(1)的 alias method 抽样算法思想。

  下面举例说明:

  比如 a,b,c,d 的概率分别为 0.1,0.2,0.3,0.4。如何编程实现按概率抽样呢?

  最简单的方法是生成一个数组:1,2,2,3,3,3,4,4,4,4。然后随机生成一个不大于4的数。这种方法简单易实现,但当随机变量很多时,占用的空间就太大了。

  再进一步,可以根据它们的概率密度分布(PDF)生成累积分布(CDF):0.1,0.3,0.6,1。然后生成不大于1的随机数,看它落在哪个区间。但这需要与临界点进行比较,

我们知道,插入有序数列最好的时间复杂度为O(logn)。

  而alias method可以实现以运行复杂度为O(1)的方式抽样。当然它需要预处理,预处理的时间复杂度为O(n),但实际过程中,尤其是重复跑的时候,运行时间复杂度才是重要的。

  alias method 怎么处理的呢?我们知道等概率分布抽样的时间复杂度为O(1),如果二项不等概率分布抽样的时间复杂度也为O(1)。如果a,b,c,d 概率分布均为0.25,那我们随机

生成1,2,3,4,抽中哪个就是哪个,复杂度自然为O(1)。如果只有两个变量,比如 a,b 概率分布为 0.2,0.8.那我们用CDF的方法,小于0.2就是a, 大于就是b,只需比较一次,

所以复杂度也是O(1)。alias method就是把这两种方法结合起来.

  首先我们把原概率分布乘以N(为后面的拼接做准备),这里是N=4。得到:0.4,0.8,1.2,1.6。

然后我们把它拼成等概率分布和二项不等概率分布:

  注意拼接的过程中,最多每一列最多有两种颜色。乘以N就保证了一定可以存在这样的拼法。具体证明见引用[1],图片出自引用[2]。

  怎么抽样呢?

  首先我们以等概率分布抽每一列。不失一般性,假设我们抽中了第三列。然后进行二项分布抽样,如果小于0.6,就是褐色,也就是c,反之,就是红色,也就是d.

这两个操作的复杂度均为O(1),故总时间复杂度也为O(1)。

  怎么保证正确性呢?

  以样本d,举例,原来抽中c的概率为0.4。运用alias method方法后,抽中c的概率为(0.25+ 0.25 * 0.2 + 0.25 * 0.4) = 0.4。事实上,等比例放大后再拆分,

其概率分布比例并没有变,结果当然也不会变。具体证明见引用[1].

引用:

1, http://www.keithschwarz.com/darts-dice-coins/

2, http://blog.csdn.net/sky_zhe/article/details/10051967

alias sample method——运行时间复杂度为O(1)的抽样算法的更多相关文章

  1. 时间复杂度为O(nlogn)的LIS算法

    时间复杂度为 n*logn的LIS算法是用一个stack维护一个最长递增子序列 如果存在 x < y 且  a[x] > a[y],那么我们可以用a[y]去替换a[x] 因为a[y]比较小 ...

  2. 时间复杂度为O(nlogn)的排序算法

    时间复杂度为O(nlogn)的排序算法(归并排序.快速排序),比时间复杂度O(n²)的排序算法更适合大规模数据排序. 归并排序 归并排序的核心思想 采用"分治思想",将要排序的数组 ...

  3. Alias sample(别名采样)

    应用场景:加权采样,即按照随机事件出现的概率抽样 具体算法: 举例如上,随机事件出现的概率依次是1/2,1/3,1/12,1/12;记随机事件的个数为N,则所有事件概率乘以N后概率为2,4/3,1/3 ...

  4. 平均时间复杂度为O(nlogn)的排序算法

    本文包括 1.快速排序 2.归并排序 3.堆排序 1.快速排序 快速排序的基本思想是:采取分而治之的思想,把大的拆分为小的,每一趟排序,把比选定值小的数字放在它的左边,比它大的值放在右边:重复以上步骤 ...

  5. Java虚拟机运行时数据区域及垃圾回收算法

    程序计数器 记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空). Java 虚拟机栈 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表.操作数栈.动态链接.方法出口 ...

  6. (转)递归算法的时间复杂度终结篇与Master method

    开篇前言:为什么写这篇文章?笔者目前在学习各种各样的算法,在这个过程中,频繁地碰到到递归思想和分治思想,惊讶于这两种的思想的伟大与奇妙的同时,经常要面对的一个问题就是,对于一个给定的递归算法或者用分治 ...

  7. python-Day5-深入正则表达式--冒泡排序-时间复杂度 --常用模块学习:自定义模块--random模块:随机验证码--time & datetime模块

    正则表达式   语法:             mport re #导入模块名 p = re.compile("^[0-9]") #生成要匹配的正则对象 , ^代表从开头匹配,[0 ...

  8. 关于乱序(shuffle)与随机采样(sample)的一点探究

    最近一个月的时间,基本上都在加班加点的写业务,在写代码的时候,也遇到了一个有趣的问题,值得记录一下. 简单来说,需求是从一个字典(python dict)中随机选出K个满足条件的key.代码如下(py ...

  9. java.lang.reflect.Method.getAnnotation()方法示例【通过反射获取到方法对象再获取方法对象上的注解信息】

    转: java.lang.reflect.Method.getAnnotation()方法示例 java.lang.reflect.Method.getAnnotation(Class <T&g ...

随机推荐

  1. Problem A 栈

    Description   You are given a string consisting of parentheses () and []. A string of this type is s ...

  2. linux命令每日一练习-pwd,cd

    pwd显示当前路径. pwd -P没能明白什么意思, cd 进入目录 cd ..返回上级目录

  3. 【转】CentOS yum安装和卸载软件的使用方法

    在CentOS yum安装和卸载软件的使用方法安装方法安装一个软件时.   CentOS yum -y install httpd安装多个相类似的软件时   CentOS yum -y install ...

  4. Jquery实现的Tabs标签页

    效果图: HTML: <div class="tabs"> <ul id="tabs"> <li class="tab- ...

  5. 分析一个嵌入payload的恶意.lnk文件

    原文:https://isc.sans.edu/diary/Analyzis+of+a+Malicious+.lnk+File+with+an+Embedded+Payload/20763 We re ...

  6. static声明初始化块的一下注意事项

    通过输出结果,我们可以看到,程序运行时静态初始化块最先被执行,然后执行普通初始化块,最后才执行构造方法.由于静态初始化块只在类加载时执行一次,所以当再次创建对象 hello2 时并未执行静态初始化块.

  7. LeetCode222 Count Complete Tree Nodes

    对于一般的二叉树,统计节点数目遍历一遍就可以了,但是这样时间复杂度O(n),一下就被卡住了. 这题首先要明白的是,我们只需要知道叶子节点的数目就能统计出总节点树. 想法1: 既然是完全二叉树,我肯定是 ...

  8. struts2中form提交到action中的中文参数乱码问题解决办法(包括取中文路径)

    我的前台页是这样的: <body>      <form action="test.action" method="post">     ...

  9. VS调试Ajax

    VS调试Ajax: 1.ashx在后台处理程序中设定断点 2.触发AJAX 3.F12打开浏览器调试,搜索找到ajax调用的JS,设置断点,在浏览器中单步调试,会自动进入后台处理程序,然后就可以调试后 ...

  10. Linux学习之CentOS--FTP服务原理及vsfptd的安装、配置

    本篇随笔将讲解FTP服务的原理以及vsfptd这个最常用的FTP服务程序的安装与配置... 一.FTP服务原理 FTP(File Transfer Protocol)是一个非常古老并且应用十分广泛的文 ...