Pouring water using Scala - algorithm

I am trying to solve the pouring water problem from codechef using scala. The problem statement is as follows:
Given two vessels, one of which can accommodate a liters of water and
the other which can accommodate b liters of water, determine the
number of steps required to obtain exactly c liters of water in one of
the vessels.
At the beginning both vessels are empty. The following operations are
counted as 'steps':
emptying a vessel,
filling a vessel,
pouring water from one vessel to the other, without spilling, until one of the vessels is either full or empty.
Input
An integer t, 1<=t<=100, denoting the number of test cases, followed
by t sets of input data, each consisting of three positive integers a
(the number of liters the first container can hold), b (the number of
liters the second container can hold), and c (the final amount of
liters of water one vessel should contain), not larger than 40000,
given in separate lines.
Output
For each set of input data, output the minimum number of steps
required to obtain c liters, or -1 if this is impossible.
Example Sample input:
2
5
2
3
2
3
4
Sample output:
2
-1
I am approaching this problem as a graph theory problem. Given the initial configuration of containers to be (0, 0), I get the next state of the containers by applying the operations:
FillA, FillB, PourAtoB, PourBtoA, EmptyA, EmptyB recursively until the target is reached.
My code is as follows:
import scala.collection.mutable.Queue
def pour(initA:Int, initB:Int, targetCapacity:Int) {
var pourCombinations = new scala.collection.mutable.HashMap[(Int, Int),Int]
val capacityA = initA
val capacityB = initB
val processingQueue = new Queue[(Int, Int, Int, Int)]
def FillA(a:Int, b:Int) = {
(capacityA, b)
}
def FillB(b:Int, a:Int) = {
(a, capacityB)
}
def PourAtoB(a:Int, b:Int): (Int, Int) = {
if((a == 0) || (b == capacityB)) (a, b)
else PourAtoB(a - 1, b + 1)
}
def PourBtoA(b:Int, a:Int): (Int, Int) = {
if((b == 0) || (a == capacityA)) (a, b)
else PourBtoA(b - 1, a + 1)
}
def EmptyA(a:Int, b:Int) = {
(0, b)
}
def EmptyB(a:Int, b:Int) = {
(a, 0)
}
processingQueue.enqueue((0, 0, targetCapacity, 0))
pourCombinations((0, 0)) = 0
def pourwater(a:Int, b:Int, c:Int, numSteps:Int): Int = {
println(a + ":" + b + ":" + c + ":" + numSteps)
if((a == c) || (b == c)) {return numSteps}
if(processingQueue.isEmpty && (pourCombinations((a,b)) == 1)) {return -1}
//Put all the vals in a List of tuples
val pStateList = scala.List(FillA(a, b), FillB(a, b), PourAtoB(a, b), PourBtoA(b, a), EmptyA(a, b), EmptyB(a, b))
pStateList.foreach{e =>
{
if(!pourCombinations.contains(e)) {
pourCombinations(e) = 0
processingQueue.enqueue((e._1, e._2, c, numSteps + 1))
}
}
}
pourCombinations((a, b)) = 1
val processingTuple = processingQueue.dequeue()
pourwater(processingTuple._1, processingTuple._2, processingTuple._3, processingTuple._4)
}
val intialvalue = processingQueue.dequeue()
pourwater(intialvalue._1, intialvalue._2, intialvalue._3, intialvalue._4)
}
There are a couple of issues with this, first of all I am not sure if I have my base cases of my recursive step set-up properly. Also, it might be that I am not using the proper Scala conventions to solve this problem. Also, I want the pour function to return the numSteps once it is finished executing. It is not doing that at the moment.
It will be great if somebody can go through my code and point out the mistakes with my approach.
Thanks

Related

Change x,y from 1,1 to p,q using given rules

Given a = p, b = q
In one cycle a can change to a = a + b or b = b + a
In any cycle either of two can be performed but not both.
Starting from a = 1, b = 1
Calculate no of iterations required to convert (x, y) from (1, 1) to (p,q) using the above mentioned rules.
Return not possible if cannot be done
Can anyone tell how to solve this problem.
As already mentioned in a comment you can just go backwards. The larger element must be the one where the calculation was performed. So you could just do the reverse on the larger element and see if you end up with (1, 1). Or better subtract the smaller element directly as many times as needed from the larger one so that it becomes smaller than the other one:
function steps(a, b) {
let count = 0
while (a != b) {
console.log('(' + a + ', ' + b + ')')
let t
if (a > b) {
t = a % b == 0 ? a / b - 1 : Math.floor(a / b)
a -= t * b
} else {
t = b % a == 0 ? b / a - 1 : Math.floor(b / a)
b -= t * a
}
count += t
}
if (a == 1)
return count
return -1
}
console.log(steps(87, 13))
console.log(steps(23, 69))

Tail recursive solution in Scala for Linked-List chaining

I wanted to write a tail-recursive solution for the following problem on Leetcode -
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contains a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example:
*Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)*
*Output: 7 -> 0 -> 8*
*Explanation: 342 + 465 = 807.*
Link to the problem on Leetcode
I was not able to figure out a way to call the recursive function in the last line.
What I am trying to achieve here is the recursive calling of the add function that adds the heads of the two lists with a carry and returns a node. The returned node is chained with the node in the calling stack.
I am pretty new to scala, I am guessing I may have missed some useful constructs.
/**
* Definition for singly-linked list.
* class ListNode(_x: Int = 0, _next: ListNode = null) {
* var next: ListNode = _next
* var x: Int = _x
* }
*/
import scala.annotation.tailrec
object Solution {
def addTwoNumbers(l1: ListNode, l2: ListNode): ListNode = {
add(l1, l2, 0)
}
//#tailrec
def add(l1: ListNode, l2: ListNode, carry: Int): ListNode = {
var sum = 0;
sum = (if(l1!=null) l1.x else 0) + (if(l2!=null) l2.x else 0) + carry;
if(l1 != null || l2 != null || sum > 0)
ListNode(sum%10,add(if(l1!=null) l1.next else null, if(l2!=null) l2.next else null,sum/10))
else null;
}
}
You have a couple of problems, which can mostly be reduced as being not idiomatic.
Things like var and null are not common in Scala and usually, you would use a tail-recursive algorithm to avoid that kind of things.
Finally, remember that a tail-recursive algorithm requires that the last expression is either a plain value or a recursive call. For doing that, you usually keep track of the remaining job as well as an accumulator.
Here is a possible solution:
type Digit = Int // Refined [0..9]
type Number = List[Digit] // Refined NonEmpty.
def sum(n1: Number, n2: Number): Number = {
def aux(d1: Digit, d2: Digit, carry: Digit): (Digit, Digit) = {
val tmp = d1 + d2 + carry
val d = tmp % 10
val c = tmp / 10
d -> c
}
#annotation.tailrec
def loop(r1: Number, r2: Number, acc: Number, carry: Digit): Number =
(r1, r2) match {
case (d1 :: tail1, d2 :: tail2) =>
val (d, c) = aux(d1, d2, carry)
loop(r1 = tail1, r2 = tail2, d :: acc, carry = c)
case (Nil, d2 :: tail2) =>
val (d, c) = aux(d1 = 0, d2, carry)
loop(r1 = Nil, r2 = tail2, d :: acc, carry = c)
case (d1 :: tail1, Nil) =>
val (d, c) = aux(d1, d2 = 0, carry)
loop(r1 = tail1, r2 = Nil, d :: acc, carry = c)
case (Nil, Nil) =>
acc
}
loop(r1 = n1, r2 = n2, acc = List.empty, carry = 0).reverse
}
Now, this kind of recursions tends to be very verbose.
Usually, the stdlib provide ways to make this same algorithm more concise:
// This is a solution that do not require the numbers to be already reversed and the output is also in the correct order.
def sum(n1: Number, n2: Number): Number = {
val (result, carry) = n1.reverseIterator.zipAll(n2.reverseIterator, 0, 0).foldLeft(List.empty[Digit] -> 0) {
case ((acc, carry), (d1, d2)) =>
val tmp = d1 + d2 + carry
val d = tmp % 10
val c = tmp / 10
(d :: acc) -> c
}
if (carry > 0) carry :: result else result
}
Scala is less popular on LeetCode, but this Solution (which is not the best) would get accepted by LeetCode's online judge:
import scala.collection.mutable._
object Solution {
def addTwoNumbers(listA: ListNode, listB: ListNode): ListNode = {
var tempBufferA: ListBuffer[Int] = ListBuffer.empty
var tempBufferB: ListBuffer[Int] = ListBuffer.empty
tempBufferA.clear()
tempBufferB.clear()
def listTraversalA(listA: ListNode): ListBuffer[Int] = {
if (listA == null) {
return tempBufferA
} else {
tempBufferA += listA.x
listTraversalA(listA.next)
}
}
def listTraversalB(listB: ListNode): ListBuffer[Int] = {
if (listB == null) {
return tempBufferB
} else {
tempBufferB += listB.x
listTraversalB(listB.next)
}
}
val resultA: ListBuffer[Int] = listTraversalA(listA)
val resultB: ListBuffer[Int] = listTraversalB(listB)
val resultSum: BigInt = BigInt(resultA.reverse.mkString) + BigInt(resultB.reverse.mkString)
var listNodeResult: ListBuffer[ListNode] = ListBuffer.empty
val resultList = resultSum.toString.toList
var lastListNode: ListNode = null
for (i <-0 until resultList.size) {
if (i == 0) {
lastListNode = new ListNode(resultList(i).toString.toInt)
listNodeResult += lastListNode
} else {
lastListNode = new ListNode(resultList(i).toString.toInt, lastListNode)
listNodeResult += lastListNode
}
}
return listNodeResult.reverse(0)
}
}
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions, explanations, efficient algorithms with a variety of languages, and time/space complexity analysis in there.

how to fast judge two double set intersect or not?

I want to fast judge two double set intersect or not.
problem
The element in set can be all range. The element in set are not ordered. Each set have 100,000+ element.
If exist a double a from set A, a double b from set B, a and b is very close,for example abs(a-b)<1e-6, we say set A and B intersect.
My way
calculate the range(lower bound and upper bound) of set_A and set_B
O(n), n is set's size
calculate range intersection rang_intersect of range_A and range_B
O(1)
if rang_intersect empty two set not intersect.
O(1)
if range_intersect not empty, find sub_set_A from set_A which in the range_intersect, find sub_set_B from set_B which in the range_intersect
O(n)
sort sub_set_A and sub_set_B
O(mlogm) m is sub_set_A's size
tranvers sub_set_A_sorted and sub_set_B_sorted by two pointer. find if exist element close, if exist two set intersect, if not, two set not intersect.
O(m)
My way can works, but I wonder if I can do faster.
Appendix
Why I want this:
Actually I am face a problem to judge two point set A & B collision or not. Each point p in point set have a double coordinate x,y,z. If exist a point a from point set A, a point b from point set B, a and b's coordinate very close, we say point set A and B collision.
In 3d case, we can define the order of point by first compare x then compare y, last compare z.
We can define the close that if all dimension's coordinate is close , the two point close.
This problem can convert to the problem above.
Some idea by gridding the space:
Let's take the point (1.2, 2.4, 3.6) with minimial distance required 1.
We may say that this point "touches" 8 unit cubes of R^3
[
(1.0, 2.0, 3.5)
(1.0, 2.0, 4.0)
(1.0, 2.5, 3.5) // 1 < 1.2 < 1.5
(1.0, 2.5, 4.0) // 2 < 2.4 < 2.5
(1.5, 2.0, 3.5) // 3.5 < 3.6 < 4
(1.5, 2.0, 4.0)
(1.5, 2.5, 3.5)
(1.5, 2.5, 4.0)
]
If two points are close to each other, their will be connected by some of their cube.
y
^
|
3 +---+---+
| | |
2.5+-------+---+---+
| a | | c | b |
2 +---+---+---+---+--->x
1 1.5 2
In example above in 2D plan, a is (1.2, 2.4).
Say b is (2.5, 2.4). b will touch the square (2,2), but a does not.
So they are not connected (indeed the min distance possible is (2.5-1.5===1).
Say c is (2.45, 2.4). c touches the square (1.5, 2). So is a. We check.
The main idea is to associate to each point its 8 cubes.
We can associate a uniq hash to each cube: the top level coordinate. e.g "{x}-{y}-{z}"
To check if A intersects B:
we build for each point of A its 8 hashes and store them in a hashmap: hash->point
for each point of B, we build the hashes, and if one of those exist in the hashmap we check if the corresponding points are in relation
Now consider
y
^
|
3 +---+---+
| a2| |
2.5+-------+
| a1| |
2 +---+---+
1 1.5 2
a2 and a1 's hashes will overlap on squares (1,2) and (1,2.5). So the hashmap is actually hash->points.
This implies that worst case could be O(n^2) if all the points land into the same cubes. Hopefully in reality they won't?
Below a code with irrelevant data:
(put 10**4 to avoid freezing the ui)
function roundEps (c, nth) {
const eps = 10**-nth
const r = (c % eps)
const v = (r >= eps / 2) ? [c-r+eps/2, c-r+eps] : [c-r, c-r+eps/2]
return v.map(x => x.toFixed(nth + 1))
}
function buildHashes (p, nth) {
return p.reduce((hashes, c) => {
const out = []
hashes.forEach(hash => {
const [l, u] = roundEps(c, nth)
out.push(`${hash},${l}`, `${hash},${u}`)
})
return out
},[''])
}
function buildMap (A, nth) {
const hashToPoints = new Map()
A.forEach(p => {
const hashes = buildHashes(p, nth)
hashes.forEach(hash => {
const v = hashToPoints.get(hash) || []
v.push(p)
hashToPoints.set(hash, v)
})
})
return hashToPoints
}
function intersects (m, b, nth, R) {
let processed = new Set()
return buildHashes(b, nth).some(hash => {
if (!m.has(hash)) return
const pts = m.get(hash)
if (processed.has(pts)) return
processed.add(pts)
return pts.some(p => R(p, b))
})
}
function d (a, b) {
return a.reduce((dist, x, i) => {
return Math.max(dist, Math.abs(x-b[i]))
}, 0)
}
function checkIntersection (A, B, nth=2) {
const m = buildMap(A, nth)
return B.some(b => intersects(m, b, nth, (a,b) => d(a, b) < 10**(-nth)))
}
// ephemeral testing :)
/*
function test () {
const assert = require('assert')
function testRound () {
assert.deepEqual(roundEps(127.857, 2), ['127.855', '127.860'])
assert.deepEqual(roundEps(127.853, 2), ['127.850', '127.855'])
assert.deepEqual(roundEps(127.855, 2), ['127.855', '127.860'])
}
function testD () {
assert.strictEqual(d([1,2,3],[5,1,2]), 4)
assert.strictEqual(d([1,2,3],[0,1,2]), 1)
}
function testCheckIntersection () {
{
const A = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
const B = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
assert(checkIntersection(A, B))
}
{
const A = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
const B = [[10,20,30]]
assert(!checkIntersection(A, B))
}
{
const A = [[0,0,0]]
const B = [[0,0,0.06]]
assert(!checkIntersection(A, B, 2))
}
{
const A = [[0,0,0.013]]
const B = [[0,0,0.006]]
assert(checkIntersection(A, B, 2))
}
}
testRound()
testD()
testCheckIntersection()
}*/
const A = []
const B = []
for (let i = 0; i < 10**4; ++i) {
A.push([Math.random(), Math.random(), Math.random()])
B.push([Math.random(), Math.random(), Math.random()])
}
console.time('start')
console.log('intersect? ', checkIntersection(A, B, 6))
console.timeEnd('start')

MergeSort in scala

I came across another codechef problem which I am attempting to solve in Scala. The problem statement is as follows:
Stepford Street was a dead end street. The houses on Stepford Street
were bought by wealthy millionaires. They had them extensively altered
so that as one progressed along the street, the height of the
buildings increased rapidly. However, not all millionaires were
created equal. Some refused to follow this trend and kept their houses
at their original heights. The resulting progression of heights was
thus disturbed. A contest to locate the most ordered street was
announced by the Beverly Hills Municipal Corporation. The criteria for
the most ordered street was set as follows: If there exists a house
with a lower height later in the street than the house under
consideration, then the pair (current house, later house) counts as 1
point towards the disorderliness index of the street. It is not
necessary that the later house be adjacent to the current house. Note:
No two houses on a street will be of the same height For example, for
the input: 1 2 4 5 3 6 The pairs (4,3), (5,3) form disordered pairs.
Thus the disorderliness index of this array is 2. As the criteria for
determining the disorderliness is complex, the BHMC has requested your
help to automate the process. You need to write an efficient program
that calculates the disorderliness index of a street.
A sample input output provided is as follows:
Input: 1 2 4 5 3 6
Output: 2
The output is 2 because of two pairs (4,3) and (5,3)
To solve this problem I thought I should use a variant of MergeSort,incrementing by 1 when the left element is greater than the right element.
My scala code is as follows:
def dysfunctionCalc(input:List[Int]):Int = {
val leftHalf = input.size/2
println("HalfSize:"+leftHalf)
val isOdd = input.size%2
println("Is odd:"+isOdd)
val leftList = input.take(leftHalf+isOdd)
println("LeftList:"+leftList)
val rightList = input.drop(leftHalf+isOdd)
println("RightList:"+rightList)
if ((leftList.size <= 1) && (rightList.size <= 1)){
println("Entering input where both lists are <= 1")
if(leftList.size == 0 || rightList.size == 0){
println("One of the lists is less than 0")
0
}
else if(leftList.head > rightList.head)1 else 0
}
else{
println("Both lists are greater than 1")
dysfunctionCalc(leftList) + dysfunctionCalc(rightList)
}
}
First off, my logic is wrong,it doesn't have a merge stage and I am not sure what would be the best way to percolate the result of the base-case up the stack and compare it with the other values. Also, using recursion to solve this problem may not be the most optimal way to go since for large lists, I maybe blowing up the stack. Also, there might be stylistic issues with my code as well.
I would be great if somebody could point out other flaws and the right way to solve this problem.
Thanks
Suppose you split your list into three pieces: the item you are considering, those on the left, and those on the right. Suppose further that those on the left are in a sorted set. Now you just need to walk through the list, moving items from "right" to "considered" and from "considered" to "left"; at each point, you look at the size of the subset of the sorted set that is greater than your item. In general, the size lookup can be done in O(log(N)) as can the add-element (with a Red-Black or AVL tree, for instance). So you have O(N log N) performance.
Now the question is how to implement this in Scala efficiently. It turns out that Scala has a Red-Black tree used for its TreeSet sorted set, and the implementation is actually quite simple (here in tail-recursive form):
import collection.immutable.TreeSet
final def calcDisorder(xs: List[Int], left: TreeSet[Int] = TreeSet.empty, n: Int = 0): Int = xs match {
case Nil => n
case x :: rest => calcDisorder(rest, left + x, n + left.from(x).size)
}
Unfortunately, left.from(x).size takes O(N) time (I believe), which yields a quadratic execution time. That's no good--what you need is an IndexedTreeSet which can do indexOf(x) in O(log(n)) (and then iterate with n + left.size - left.indexOf(x) - 1). You can build your own implementation or find one on the web. For instance, I found one here (API here) for Java that does exactly the right thing.
Incidentally, the problem with doing a mergesort is that you cannot easily work cumulatively. With merging a pair, you can keep track of how out-of-order it is. But when you merge in a third list, you must see how out of order it is with respect to both other lists, which spoils your divide-and-conquer strategy. (I am not sure whether there is some invariant one could find that would allow you to calculate directly if you kept track of it.)
Here is my try, I don't use MergeSort but it seems to solve the problem:
def calcDisorderness(myList:List[Int]):Int = myList match{
case Nil => 0
case t::q => q.count(_<t) + calcDisorderness(q)
}
scala> val input = List(1,2,4,5,3,6)
input: List[Int] = List(1, 2, 4, 5, 3, 6)
scala> calcDisorderness(input)
res1: Int = 2
The question is, is there a way to have a lower complexity?
Edit: tail recursive version of the same function and cool usage of default values in function arguments.
def calcDisorderness(myList:List[Int], disorder:Int=0):Int = myList match{
case Nil => disorder
case t::q => calcDisorderness(q, disorder + q.count(_<t))
}
A solution based on Merge Sort. Not super fast, potential slowdown could be in "xs.length".
def countSwaps(a: Array[Int]): Long = {
var disorder: Long = 0
def msort(xs: List[Int]): List[Int] = {
import Stream._
def merge(left: List[Int], right: List[Int], inc: Int): Stream[Int] = {
(left, right) match {
case (x :: xs, y :: ys) if x > y =>
cons(y, merge(left, ys, inc + 1))
case (x :: xs, _) => {
disorder += inc
cons(x, merge(xs, right, inc))
}
case _ => right.toStream
}
}
val n = xs.length / 2
if (n == 0)
xs
else {
val (ys, zs) = xs splitAt n
merge(msort(ys), msort(zs), 0).toList
}
}
msort(a.toList)
disorder
}
Another solution based on Merge Sort. Very fast: no FP or for-loop.
def countSwaps(a: Array[Int]): Count = {
var swaps: Count = 0
def mergeRun(begin: Int, run_len: Int, src: Array[Int], dst: Array[Int]) = {
var li = begin
val lend = math.min(begin + run_len, src.length)
var ri = begin + run_len
val rend = math.min(begin + run_len * 2, src.length)
var ti = begin
while (ti < rend) {
if (ri >= rend) {
dst(ti) = src(li); li += 1
swaps += ri - begin - run_len
} else if (li >= lend) {
dst(ti) = src(ri); ri += 1
} else if (a(li) <= a(ri)) {
dst(ti) = src(li); li += 1
swaps += ri - begin - run_len
} else {
dst(ti) = src(ri); ri += 1
}
ti += 1
}
}
val b = new Array[Int](a.length)
var run = 0
var run_len = 1
while (run_len < a.length) {
var begin = 0
while (begin < a.length) {
val (src, dst) = if (run % 2 == 0) (a, b) else (b, a)
mergeRun(begin, run_len, src, dst)
begin += run_len * 2
}
run += 1
run_len *= 2
}
swaps
}
Convert the above code to Functional style: no mutable variable, no loop.
All recursions are tail calls, thus the performance is good.
def countSwaps(a: Array[Int]): Count = {
def mergeRun(li: Int, lend: Int, rb: Int, ri: Int, rend: Int, di: Int, src: Array[Int], dst: Array[Int], swaps: Count): Count = {
if (ri >= rend && li >= lend) {
swaps
} else if (ri >= rend) {
dst(di) = src(li)
mergeRun(li + 1, lend, rb, ri, rend, di + 1, src, dst, ri - rb + swaps)
} else if (li >= lend) {
dst(di) = src(ri)
mergeRun(li, lend, rb, ri + 1, rend, di + 1, src, dst, swaps)
} else if (src(li) <= src(ri)) {
dst(di) = src(li)
mergeRun(li + 1, lend, rb, ri, rend, di + 1, src, dst, ri - rb + swaps)
} else {
dst(di) = src(ri)
mergeRun(li, lend, rb, ri + 1, rend, di + 1, src, dst, swaps)
}
}
val b = new Array[Int](a.length)
def merge(run: Int, run_len: Int, lb: Int, swaps: Count): Count = {
if (run_len >= a.length) {
swaps
} else if (lb >= a.length) {
merge(run + 1, run_len * 2, 0, swaps)
} else {
val lend = math.min(lb + run_len, a.length)
val rb = lb + run_len
val rend = math.min(rb + run_len, a.length)
val (src, dst) = if (run % 2 == 0) (a, b) else (b, a)
val inc_swaps = mergeRun(lb, lend, rb, rb, rend, lb, src, dst, 0)
merge(run, run_len, lb + run_len * 2, inc_swaps + swaps)
}
}
merge(0, 1, 0, 0)
}
It seems to me that the key is to break the list into a series of ascending sequences. For example, your example would be broken into (1 2 4 5)(3 6). None of the items in the first list can end a pair. Now you do a kind of merge of these two lists, working backwards:
6 > 5, so 6 can't be in any pairs
3 < 5, so its a pair
3 < 4, so its a pair
3 > 2, so we're done
I'm not clear from the definition on how to handle more than 2 such sequences.

Integer in an interval with maximized number of trailing zero bits

Sought is an efficient algorithm that finds the unique integer in an interval [a, b] which has the maximum number of trailing zeros in its binary representation (a and b are integers > 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
}
here are some examples
bruteForce( 5, 7) == 6 // binary 110 (1 trailing zero)
bruteForce( 1, 255) == 128 // binary 10000000
bruteForce(129, 255) == 192 // binary 11000000
etc.
This one finds the number of zeros:
// 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)
}
This one returns the number with those zeros:
// 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)
}
I doubt the log(log(n)) solution has a small enough constant term to beat this. (But you could do binary search on the number of zeros to get log(log(n)).)
I decided to take Rex's challenge and produce something faster. :-)
// 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)
}
Benchmarked with
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)))
First see if there is a power of two that lies within your interval. If there is at least one, the largest one wins.
Otherwise, choose the largest power of two that is less than your minimum bound.
Does 1100000...0 lie in your bound? If yes, you've won. If it's still less than your minimum bound, try 1110000...0; otherwise, if it's greater than your maximum bound, try 1010000...0.
And so forth, until you win.
as a conclusion, here is my variant of Rex' answer which gives both the center value and also an 'extent' which is the minimum power of two distance from the center which covers both a in the one direction and b in the other.
#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() }

Resources