ZeoCool's blog

By ZeoCool, 3 months ago, In English

So yesterday I sent a code of this search in a group chat as a joke but surprisingly everyone praised it and iframe_ dubbed it as "Fenwick search" and now I am farming contribution making a blog about it.

Fenwick Search

Lets say you want to search on the interval $$$[1, n)$$$ and lets say you want to find the minimal position that satisfy a certain property. Also let the minimal position that satisfies said property be $$$ans$$$

So, in regular binary search you keep a left bound and a right bound and you change them accordingly, but in Fenwick search you only keep track of the middle bound. Firstly initialize the middle bound as the highest power of 2 that is less than n.

The gist of Fenwick search is transforming the middle bound into $$$ans$$$, we do that by manipulating the bits in the middle bound. Specifically for every set bit in $$$ans$$$ we set the bit after the last set bit in the middle bound and we shift it left until it matches the desired bit in $$$ans$$$. For example, lets say we start with middle bound $$$16$$$ and we want to transform it into $$$ans = 22$$$, $$$16_{10} = 10000_2 \rightarrow 11000_2 \rightarrow 10100_2 \rightarrow 10110_2 = 22_{10} $$$

But how do we actually transform it? Well its simple, we check if the middle bound satisfies the property if so, you shift the last set bit one to the left, if it does not satisfy you need to set a new bit after the last set. You need to perform this checking and updating exactly $$$\lfloor{log2(n - 1)}\rfloor$$$. There is a single edge case, if the middle bound is $$$\ge n$$$ you always need to shift the last bit to the right.

To set a new bit after the last set you need to update the middle bound like this: $$$m = m + (m \ \text{&} \ (-m)) \gt \gt 1$$$ and to shift the last bit to the right you need to update the middle bound like this:$$$m = m - (m \ \text{&} \ (-m)) \gt \gt 1$$$

int tm = 1<<(LOG);
int ans = -1;
for(int i = 0;i < LOG;i++){
    if(tm >= n)tm -= (tm & -tm) >> 1;
    else if(check(tm)){
        ans = tm;
        tm -= (tm & -tm) >> 1;
    }else tm += (tm & -tm) >> 1;
}

This is the code, $$$check(x)$$$ is function that checks the property the we are searching

That's it, sorry for my bad english I tried to make it as good as possible

  • Vote: I like it
  • +23
  • Vote: I do not like it

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by ZeoCool (previous revision, new revision, compare).

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by ZeoCool (previous revision, new revision, compare).

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by ZeoCool (previous revision, new revision, compare).

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Pretty sure this technique is called binary lifting on fenwick tree

Relevant topic: https://codeforces.me/blog/entry/61364

»
2 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Bro really thinks he's him

»
2 months ago, # |
  Vote: I like it +25 Vote: I do not like it

This is well known, and most people I know binary search this way already. See https://cp-algorithms.com/num_methods/binary_search.html#search-with-powers-of-2 but it is cool you rediscovered it.

»
2 months ago, # |
  Vote: I like it 0 Vote: I do not like it

It's a common variant for binary search. To simplify implementation you also don't need to set step sizes to exact powers of $$$2$$$, so long as they are exponentially shrinking.

I first encountered it at https://darrenyao.com/usacobook/cpp.pdf#page=64