具有频繁变化概率的c++离散分布抽样

c++ discrete distribution sampling with frequently changing probabilities

本文关键字:分布 c++ 变化 概率      更新时间:2023-10-16

问题:我需要从一个由某些权重构成的离散分布中抽样,例如{w1,w2,w3,…},则概率分布{p1,p2,p3,…},其中pi=wi/(w1+w2+…)

一些wi的变化非常频繁,但仅占所有wi的非常低的比例。但是每次发生这种情况时,分布本身都必须重新规范化,因此我认为Alias方法不能有效地工作,因为每次都需要从头开始构建整个分布。

我目前正在考虑的方法是二叉树(堆方法),其中所有wi都保存在最低级别,然后在更高级别中每个两个的总和,以此类推。它们的总和将处于最高水平,这也是一个归一化常数。因此,为了在wi变化后更新树,需要进行log(n)次变化,以及从分布中获得相同数量的样本。

问题:

Q1。你有更好的主意如何更快地实现它吗?Q2。最重要的部分:我正在寻找一个已经做到这一点的库。

解释:几年前我自己就这样做了,通过在vector中构建堆结构,但从那以后我学到了很多东西,包括发现库(:))和容器,比如map…现在我需要用更高级的功能重写这段代码,这次我想让它正确:

那么Q2.1是否有一种很好的方法来使c++映射排序和搜索不是通过索引,而是通过它的元素的累积和(这就是我们如何采样,对吗?…)。(这是我目前的理论,我想这样做,但它不一定是这样的…)

也许有更好的方法来做同样的事情?我相信这个问题是如此频繁,以至于我很惊讶我找不到某种库来帮我解决这个问题。

非常感谢,如果这个问题以其他形式被问到,我很抱歉,请指导我,但我花了很长时间寻找……

- z

编辑:有一种可能性,我可能需要删除或添加元素,但我认为我可以避免它,如果这会产生巨大的差异,从而只留下改变权重的值。

Edit2:一般来说,权重是实数,我必须考虑如果我能使它们成为整数……

我实际上会使用字符串的散列集(不要记住它的c++容器,但您可能需要实现自己的容器)。为每个i放置wi个元素,值为"w1_1","w1_2",…全部通过"w1_[w1]"(即以"w1_"开头的w1个元素)。

当你需要抽样时,使用均匀分布随机选取一个元素。如果你选择了w5_*,假设你选择了元素5。由于哈希中元素的数量,这将为您提供您正在寻找的分布。

现在,当wi从A变为B时,只需向哈希中添加B-A元素(如果B>A),或者删除wi中最后的A-B元素(如果A>B)。

在这种情况下,添加新元素和删除旧元素是微不足道的。

显然,问题是"随机选择一个元素"。如果你的哈希值是封闭哈希值,你就随机选择一个数组单元格,如果它是空的——就再随机选择一个。如果你保持你的哈希值比总权重的总和大3或4倍,你的复杂度将是相当不错的:O(1)用于检索随机样本,O(| a - b |)用于修改权重。

另一个选择,因为只有一小部分权重会改变,是将权重分成两个——固定的部分和改变的部分。那么你只需要担心变化部分的变化,以及变化部分的总重量与不变部分的总重量之差。然后对于固定部分,你的哈希变成了一个简单的数字数组:1出现了w1次,2出现了w2次,等等……

选择一个随机的固定元素就是选择一个随机的数字。

当您更改一个值时更新您的规范化因子是微不足道的。这可能是一种算法。

w_sum = w_sum_old - w_i_old + w_i_new;

如果您将p_i保留为计算属性p_i = w_i/w_sum,则可以避免每次需要计算p_i时重新计算整个p_i数组。但是,您可以更新许多统计属性,而无需重新计算整个总和

expected_something = (something_1 * w_1 + something_2 * w_2 + ...) / w_sum;

通过一些代数运算,你可以更新expected_something,方法是减去旧权重的贡献值,再加上新权重的贡献值,并根据需要与归一化因子相乘和除。

如果你在抽样过程中跟踪哪些结果是样本的一部分,就有可能传播概率是如何更新到生成的样本的。这是否可以使您更新而不是重新计算与样本相关的值?我认为位图可以提供一种有效的方式来存储用于构建样本的结果的索引。

将概率与总和存储在一起的一种方法是从所有概率开始。在接下来的N/2个位置中存储对的和。然后是N/4对的和。显然,这些和的位置可以在O(1)时间内计算出来。这个数据结构有点像堆,不过是倒过来的。