动态(即可变大小)芬威克树?

时间:2014-02-24 18:26:58

标签: c++ algorithm tree stochastic-process

问题: 我偶然发现了Fenwick树(二元索引树),它可以轻松计算累积总和。但是,我只发现了leeves(summands)数量不变的实现(但它们的值可以改变)。 是否有类似广义Fe​​nwick树的东西允许改变背风(加号)的数量,即具有可变大小?

背景 我目前正在编写一些随机模拟代码(用C ++编写):在一个瓮中有球,每个球我有一定的概率p_i被绘制。在绘图事件中,球被绘制(并被移除)并被具有新概率的两个新球替换(并且相应地重新调整所有概率;我已经有效地“重新缩放”,因此不要打扰它)。在某些时候,我开始移除球,使得球的数量围绕恒定值(之前已知)波动。为了有效地进行绘图,我想使用二叉树。标准的Fenwick树完全符合我的要求,只是它不允许更改urn中的球数。

典型数字 从10个球开始,添加球并开始移除球,一旦有大约1000球,那么在球洞中有900到1100个球(即球被添加和移除,使得数量保持在1000左右)。

到目前为止的解决方法 估计所需的球的最大数量(具有一些安全边界,比如1200个球)并使得大小的恒定大小的Fenwick树具有大部分球,最初具有概率0并且被连续更新。

非常感谢你的帮助! 的Matthias

1 个答案:

答案 0 :(得分:7)

实际上,正常(不以任何方式概括)Fenwick树允许随时增加叶子的数量。

某些特定实现可能不允许它。但这可以修复。例如,implementation from TopCoder不允许更改叶子数。问题是update函数修改了从给定索引开始向上的数组元素,当它达到某个限制(MaxVal)时停止,在我们的例子中,这是预先不知道的。 read函数迭代向下的数组元素,因此不需要知道当前的数组大小。如果我们在updateread之间交换数组迭代代码,则可以修复此问题:现在update不需要知道MaxValMaxVal用于read,我们可以使用最大的更新索引MaxVal

int read(int idx){
    int sum = 0;
    while (idx <= MaxVal){
        sum += tree[idx];
        idx += (idx & -idx);
    }
    return sum;
}

void update(int idx ,int val){
    while (idx > 0){
        tree[idx] += val;
        idx -= (idx & -idx);
    }
}

注释

  1. 与TopCoder(其中read返回前缀sum)的实现不同,此实现提供后缀sum。如果您需要前缀和,只需从总值中减去read返回的值。
  2. 我选择了这个实现,因为(1)它是对着名的TopCoder实现的简单修改,(2)它以非常对称的方式更新索引,所以仅仅将'+'改为' - '就足够了从前缀到后缀。
  3. 否则我宁愿在索引计算中使用不同的按位运算。恕我直言这个博客:Fenwick trees demystified建议一个更好的选择,每个索引更新只有2个操作,而不是3(但也需要一些修改以允许可变大小)。如果不考虑兼容性,我们可以通过使用最新英特尔指令集(BMI1)中的BLSR等特定指令来做得更好。