在具有最大尾随零位数的区间中的整数

时间:2011-05-27 18:49:56

标签: algorithm scala bit-manipulation

Sought是一种有效的算法,可以在区间[a, b]中找到唯一的整数,该区间在其二进制表示中具有最大尾随零(ab是整数> 0 ):

def bruteForce(a: Int, b: Int): Int =
  (a to b).maxBy(Integer.numberOfTrailingZeros(_))

def binSplit(a: Int, b: Int): Int = {
  require(a > 0 && a <= b)
  val res = ???
  assert(res == bruteForce(a, b))
  res
}

这里有一些例子

bruteForce(  5,   7) ==   6 // binary 110 (1 trailing zero)
bruteForce(  1, 255) == 128 // binary 10000000
bruteForce(129, 255) == 192 // binary 11000000

4 个答案:

答案 0 :(得分:5)

这个找到零的数量:

// Requires a>0
def mtz(a: Int, b: Int, mask: Int = 0xFFFFFFFE, n: Int = 0): Int = {
  if (a > (b & mask)) n
  else mtz(a, b, mask<<1, n+1)
}

这个用这些零返回数字:

// Requires a > 0
def nmtz(a: Int, b: Int, mask: Int = 0xFFFFFFFE): Int = {
  if (a > (b & mask)) b & (mask>>1)
  else nmtz(a, b, mask<<1)
}

我怀疑log(log(n))解决方案有一个足够小的常数项来击败它。 (但你可以对零的数量进行二进制搜索以得到log(log(n))。)

答案 1 :(得分:4)

我决定接受雷克斯的挑战并更快地制作出一些东西。 : - )

// requires a > 0
def mtz2(a: Int, b: Int, mask: Int = 0xffff0000, shift: Int = 8, n: Int = 16): Int = {
  if (shift == 0) if (a > (b & mask)) n - 1 else n
  else if (a > (b & mask)) mtz2(a, b, mask >> shift, shift / 2, n - shift)
  else mtz2(a, b, mask << shift, shift / 2, n + shift)
}

进行基准比较
import System.{currentTimeMillis => now}
def time[T](f: => T): T = {
  val start = now
  try { f } finally { println("Elapsed: " + (now - start)/1000.0 + " s") }
}

val range = 1 to 200
time(f((a, b) => mtz(a, b)))
time(f((a, b) => mtz2(a, b)))

答案 2 :(得分:2)

首先看看你的间隔内是否有2的力量。如果至少有一个,则最大的一个获胜。

否则,选择小于最小界限的2的最大幂。

1100000 ...... 0是否在你的范围内?如果是的话,你赢了。如果它仍然小于你的最小界限,试试1110000 ... 0;否则,如果它大于你的最大界限,试试1010000 ... 0。

等等,直到你赢了。

答案 3 :(得分:0)

作为结论,这里是我的Rex'答案的变体,它给出了中心值和“范围”,它是距离中心两个距离的最小功率,它在一个方向上覆盖了a和另一个b

@tailrec def binSplit(a: Int, b: Int, mask: Int = 0xFFFFFFFF): (Int, Int) = {
  val mask2 = mask << 1
  if (a > (b & mask2)) (b & mask, -mask)
  else binSplit(a, b, mask2)
}

def test(): Unit = {
  val Seq(r1, r2) = Seq.fill(2)(util.Random.nextInt(0x3FFFFFFF) + 1)
  val (a, b) = if (r1 <= r2) (r1, r2) else (r2, r1)
  val (center, extent) = binSplit(a, b)
  assert((center >= a) && (center <= b) && (center - extent) <= a &&
         (center - extent) >= 0 && (center + extent) > b, (a, b, center, extent))
}

for (i <- 0 to 100000) { test() }