通过位掩码进行二进制搜索?

时间:2015-10-12 23:23:46

标签: algorithm scala bit-manipulation binary-search bitmask

我多次使用此算法对IntsLongs进行二分搜索。基本上,我从Long.MinValueLong.MaxValue开始,并决定将位设置在i位置,具体取决于我最大化(或最小化)函数的值。在实践中,事实证明它更快(正好是63 * 2按位操作)并且更容易编码并避免了许多gotchas of traditional binary search implementations

这是我在Scala中的算法:

/**
 * @return Some(x) such that x is the largest number for which f(x) is true
 *         If no such x is found, return None
 */
def bitBinSearch(f: Long => Boolean): Option[Long] = {
  var n = 1L << 63
  var p = 0L
  for (i <- 62 to 0 by -1) {
    val t = 1L << i
    if (f(n + t)) n += t
    if (f(p + t)) p += t
  }
  if (f(p)) Some(p) else if (f(n)) Some(n) else None
}

我有3个问题:

  • 这个算法在文献中叫什么?当然,我不能成为这个的发明者 - 但是,当我尝试使用谷歌搜索二进制搜索+位掩码/切换的各种组合时,我没有找到任何东西。我个人称它为“bitBinSearch”。我没有在IntLong域内通过二进制搜索的文章中看到过这一点,这对于写这些内容非常简单。

  • 无论如何都可以改进/缩短代码吗?现在我跟踪np中的消极和积极解决方案。任何聪明的方法我可以将它们合并为单个变量?以下是一些示例测试用例:http://scalafiddle.net/console/70a3e3e59bc61c8eb7acfbba1073980c在您尝试回答之前

  • 是否有可以与DoubleFloat一起使用的版本?

2 个答案:

答案 0 :(得分:3)

只要你有点笨拙(在某些圈子里流行的消遣),为什么不一直走?我不知道是否有任何效率可以获得,但我认为它实际上使算法更清晰。

var

当然,如果你去递归,你可以消除那些讨厌的import scala.annotation.tailrec @tailrec def bitBinSearch( f: Long => Boolean , n: Long = Long.MinValue , p: Long = 0L , t: Long = Long.MinValue >>> 1 ): Option[Long] = { if (t > 0) bitBinSearch(f , if (f(n|t)) n|t else n , if (f(p|t)) p|t else p , t >> 1 ) else List(p,n).find(f) }

import Integral.Implicits._
import Ordering.Implicits._
def bitBinSearch[I](f: I => Boolean)(implicit ev:Integral[I]): Option[I] = {
  def topBit(x: I = ev.one):I = if (x+x < ev.zero) x else topBit(x+x)
  var t:I = topBit()
  var p:I = ev.zero
  var n:I = t+t
  while (t > ev.zero) {
    if ( f(p+t) ) p += t
    if ( f(n+t) ) n += t
    t /= (ev.one+ev.one)
  }
  List(p,n).find(f)
}

同样,可能效率不高,但可能更像Scala。

<强>更新

你对Int / Long的评论让我想知道一个函数是否可以做到这一切。

在走了几个死路之后,我终于想出了这个(奇怪的是,它实际上非常接近你的原始代码)。

assert(bitBinSearch[Byte] (_ <= 0) == Some(0))
assert(bitBinSearch[Byte] (_ <= 1) == Some(1))
assert(bitBinSearch[Byte] (_ <= -1) == Some(-1))
assert(bitBinSearch[Byte] (_ <= 100) == Some(100))
assert(bitBinSearch[Byte] (_ <= -100) == Some(-100))
assert(bitBinSearch[Short](_ <= 10000) == Some(10000))
assert(bitBinSearch[Short](_ <= -10000) == Some(-10000))
assert(bitBinSearch[Int]  (_ <= Int.MinValue) == Some(Int.MinValue))
assert(bitBinSearch[Int]  (_ <= Int.MaxValue) == Some(Int.MaxValue))
assert(bitBinSearch[Long] (_ <= Long.MinValue) == Some(Long.MinValue))
assert(bitBinSearch[Long] (_ <= Long.MaxValue) == Some(Long.MaxValue))
assert(bitBinSearch[Long] (_ < Long.MinValue) == None)

这通过了以下测试。

ModelState.IsValid

答案 1 :(得分:0)

我不知道Scala,但这是我通过java中的bitmasking进行二进制搜索的版本
我的算法是这样的

我们从最高功率为2的索引开始,以2 0 结束。每当我们看到A[itemIndex] ≤ A[index]时,我们都会更新itemIndex += index 迭代后itemIndex给出项目的索引(如果在数组中存在),则给出A中的最低值

int find(int[] A, int item) { // A uses 1 based indexing
    int index = 0;
    int N = A.length;
    for (int i = Integer.highestOneBit(N); i > 0; i >>= 1) {
        int j = index | i;
        if (j < N && A[j] <= item) {
            index = j;
            if (A[j] == item) break;
        }
    }
    return item == A[index] ? index : -1;
}