Блог пользователя AFGhazy

Автор AFGhazy, история, 11 лет назад, По-английски

Hey, Codeforces I suffered for awhile from writing efficient codes for binary searching, but recently I found an efficient way using bit masking. Say you want to maximize a result using binary search and good function that return true if it's oky or false otherwise. so our binary search would be like that:

__int64 r = 0; for(int i = 62; i >= 0; --i) { if( good(r + (1LL<<i)) ) r += (1LL<<i); }

now you can binary search with out usage of variables such as low and high and middle which would lead to infinite loops.

  • Проголосовать: нравится
  • +32
  • Проголосовать: не нравится

»
11 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

As I know it's called binary lifting, btw lower_bound and upper_bound in c++ stl uses same approach)

»
11 лет назад, скрыть # |
 
Проголосовать: нравится -8 Проголосовать: не нравится

Hmmmm...Nice...I didn't see that before...

Can we always use this instead of binary search???

»
11 лет назад, скрыть # |
 
Проголосовать: нравится -19 Проголосовать: не нравится

А как писать левый бианрный поиск таким подходом???

»
11 лет назад, скрыть # |
 
Проголосовать: нравится -17 Проголосовать: не нравится

We can modify the classic binary search to avoid these bugs.

inline int binarySearch(int v[], int n, int x)
{
    int lo = -1, hi = n, mid;     // we set both hi and lo outside our range of search

    while (hi - lo > 1)           // this invariant will keep lo and hi distinct
    {
        mid = lo + (hi - lo) / 2; // avoids addition overflow
        if (v[mid] < x)           // invariant v[lo] < x <= v[hi], assuming v[-1] = -oo and v[n] = oo
            lo = mid;
        else
            hi = mid;
    }
    if (hi == n || v[hi] != x)    // not found
        return -1;
    return x;                     // found
}

You can read more about it here.

»
11 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
int result = -1; // remains -1 if no argument is good
while (left <= right) {
  int mid = (left + right) / 2;
  if (good(mid)) {
    result = mid;
    left = mid + 1;
  } else {
    right = mid - 1;
  }
}

No infinite loops, no bugs, no thinking, only AC at the first attempt.

  • »
    »
    11 лет назад, скрыть # ^ |
     
    Проголосовать: нравится -8 Проголосовать: не нравится

    Can you elaborate a bit.And what is good(mid),a predicate function ?

    • »
      »
      »
      11 лет назад, скрыть # ^ |
       
      Проголосовать: нравится -8 Проголосовать: не нравится

      Yes, it's a predicate function, what else can it be.

      The main advantage of this version of binary search is that it always saves its structure. No unclear "invariants" with all these half-opened segments, which usually are the causes of bugs. The fact that result variable is updated as the search goes on, makes it easy to manage.

      The code above assumes that the function good() is non-increasing (1, ..., 1, 0, ..., 0). Now, say, it's non-decreasing (0, ..., 0, 1, ..., 1). Then just swap the lines left = mid + 1 and right = mid - 1 and it's done!

»
10 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Thanks, I've always been using recursive binary search and this has opened my eyes.

»
10 лет назад, скрыть # |
 
Проголосовать: нравится +3 Проголосовать: не нравится

I didn't got it what should i konw to understand this code?

»
10 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I have no idea about bit masking but i found this article very helpful to clearly understand binary search.

»
10 лет назад, скрыть # |
Rev. 2  
Проголосовать: нравится 0 Проголосовать: не нравится

Here's a clean Scala version that also works for negative numbers:

 /**
   * Binary search by bit toggling from MSB to LSB
   * O(64) bit-wise operations for Longs (O(32) for Ints)
   *
   * @return Some(x) such that x is the largest number for which f is true
   *         If no such x is found, None
   */
  def bitBinSearch(f: Long => Boolean): Option[Long] = {
    var p = 0L
    var n = Long.MinValue
    var t = n >>> 1
    while (t > 0) {
      if (f(p|t)) p |= t
      if (f(n|t)) n |= t
      t >>= 1
    }
    Seq(p, n) find f
  }

Source: https://github.com/pathikrit/scalgos/blob/c7f79824a4110397cbf340c08cae487b00ba7677/src/main/scala/com/github/pathikrit/scalgos/BitHacks.scala#L90