alias method/别名算法

前言

一个在oi/acm比较少见的算法吧。。

从问题开始QAQ

有\(N(N>1)\)个物品,有属性\(d_i\),现在要平均放进\(N-1\)个盒子里,每个盒子最多存在两个物品,物品可以分割。

当N=2时。

显然两个全放进一个盒子里就行了。

当N>2时。

显然我们可以先算出每个盒子需要放多少,设其为\(K\),于是\(\sum{d_i}=(N-1)K\)。
一共\(N\)个物品要放进\(N-1\)个盒子中,设其中\(d_i\)的最大值为\(mx\),最小值为\(mn\)。
性质1:\(mn<K\)
性质2:\(mn+mx>K\)
这两个性质比较显然,都可以用反证法证明的。
于是我们可以直接让\(mn\)和部分\(mx\)来凑成K,多余部分放回去,于是物品和盒子可数均减少1,递归即可。

怎么实现

我们需要维护增加元素,删除元素,询问最大最小值的一个数据结构,显然可以用个multiset,于是就能轻松做到O(nlogn)。

进一步优化

如果要维护最大最小值的话,就很难不带log。(悲)。然而实际上我们也并非必须取最值。
我们考虑把物品分成两类,一类是\(d_i<K\),其余是另一类。
于是我们可以每次在两个类中各取一个凑一个K,然后放回剩余。
然而这么做会有个问题,到后面可能存在所有\(d_i\)全都小于\(K\)的情况。
其实这个情况下可以证明任意两个物品都能满足加起来不小于K(就不证了吧QAQ),于是按顺序合并就好。
于是现在的做法就可以做到空间O(n),时间O(n)了。

应用

一个随机事件包含\(n\)种情况,每种情况发生的概率分别为:\(p_i\),问怎么用产生符合这个概率的采样方法。
这种问题的解决方法很多,比如按照概率构造数组然后在上面随机。。
然而许多方法时间空间复杂度不够优秀,或者在大量数据采样时不能较好的符合发生概率。
应用别名算法,相当于是把这些总和为1的物品分到n-1的盒子里,每次采样先随机到一个盒子,再按照盒子内的比例随机一次。
就能做到O(n)的空间和时间预处理,每次O(1)的采样,并且采样结果比较符合事件概率。