Ask HN: Fast sampling and update of weighted items? (data structure like red-black trees?)
What is the appropriate data structure for this task?
I have a set of N items. N is large. Each item has a positive weight value associated with it.
I would like to do the following, quickly:
inner loop:
Sample an item, according it its weight.
[process...]
Update the weight of K items, where K << N.
When I say sample by weight, this is different than uniform sampling. An item's likelihood is in proportion to its weight. So if there are two items, and one has weight .8 and one has weight .2, then they have likelihood 80% and 20% respectively.The number of items N remains fixed. Weights are in a bounded range, say [0, 1]. Weights do not always sum to one.
A naive approach takes O(n) time steps to sample. Is there an O(log(n)) algorithm?
What is the appropriate data structure for this task? I believe that red-black trees are inappropriate, since they treat each item as having equal weight.
28 comments
[ 3.8 ms ] story [ 62.3 ms ] threadThen, just generate a random number 0<x<(sum of all weights).
Finally, do a binary search on the array O(log(n)) comparing x to k.
...As for the "updating" step, you didn't define that very well or state whether you're interested in minimizing that as well- But given that the number of items may be as large as N it seems that a naive linear update would be the best you could achieve.
1) Determine the maximum weight mw. (This is O(N) the first time, O(K) subsequent times.)
2) Repeat until an index i is accepted:
3) Return the accepted index i.This gives you the desired distribution in time O(mw/aw) (maximum weight over average weight).
http://en.wikipedia.org/wiki/Rejection_sampling
In learning algorithms, the distribution of the weights can get piqued.
and how are these K items found? Do they have the same weight as the sampled item?
At the next child, if the left branch has weight 12 and the right branch has weight 38, the left branch spans 30-42 and the right branch spans 42-80. So you follow the left branch, because you want 40.
Just pick a random x from [0, sum of weights in the tree], then find a node n, such that sum of weights S to the left of n is no more than x and S+weight(n)>=x.
What would be an estimate of N : 10^3, 10^6, 10^9 ?
Funny story, that. At our lab, we are developing an optimizing compiler in Python for math expressions. You write the function you want to repeatedly evaluate in Python. Maybe there's a gradient thrown in. Our library optimizes the function graph, converts in to C, and compiles it. You can then execute your function in python, but it's fast.
We should be releasing 0.1 in a week.
for example if the possible weights are .25, .5, and .75, put all items into one of those buckets (array backed). The bucket then gets a weight of (individual weight)*(bucket size). First randomly choose a bucket based on the bucket weight, then randomly choose an object from the bucket.
First, preprocess your data:
1) Normalize the weights 2) Sort the items in descending order by weight 3) Calculate, for each item, the sum of the weights of all prior items 4) Enter the items into a binary search tree. The key into this tree is the sum you calculated in step 3. You can use whichever data structure you want here... red-black or splay would probably be best, but it depends on your situation.
Now your O(log n) lookup:
1) Generate a random variate in [0,1) 2) Search the binary tree for the item with the largest key that is <= the variate.
Ideally, you could pick smaller fraction of the array as N gets bigger, giving you sub O(N) scaling.
In other words, if the array indexes that get picked using a slow algorithm are distributed randomnly, you might as well just pick a random subsection, as long as it is big enough to have the same distribution of W's as the whole array.