Problem¶
Given a tower that have n
floor height
Given you two Crystal ball that break if being drop from b
floor and above
Find b
You only have two Crystal ball, so if you drop and breaking both, you can’t no longer testing it again
You are provided with checkBreak(x)
function, that can’t no longer be call after two True
return
class Problem():
def __init__(self, n, b):
self.n = n
self.b = b
self.count = 2
self.check = 1
def checkBreak(x):
if x >= self.b:
assert self.count > 0
self.count -= 1
return x >= self.b
def checkResult(x):
assert x == self.b
return x == self.b
Solve¶
Jumping in sqrt()¶
O(sqrt n)
By the define of the problem:
- If we used up one ball (one call that
checkBreak(x)
return true), the next one we have to do a linear search so that we can find the exactb
value. - This mean, we have to find the balancing of how much time:
Ball 2
: We spending on linear search to find the exactb
valueBall 1
: We spending quickly jump between each floor, so we can reducing total time spending onBall 2
This happen that we using sqrt(n) for jump step of Ball 1
drop.
- Because
n / sqrt(n) == sqrt(n)
, this mean we spendingsqrt(n)
time dropBall 1
as the worst case. - After
Ball 1
break, we back to last know value that Ball 1 not break and start linear search from there, this mean we only need to spend on anothersqrt(n)
range of linear search time dropBall 2
as the worst case.
Making this, a rare O(sqrt n)
time complexity solvable problem
import math
class Problem():
def __init__(self, n, b):
self.n = n
self.b = b
self.count = 2
self.check = 1
def checkBreak(self, x):
if x >= self.b:
assert self.count > 0
self.count -= 1
return x >= self.b
def checkResult(self, x):
assert x == self.b
return x == self.b
def solve(n, cb, cr):
step = math.trunc(math.sqrt(n))
curr = 0
for i in range(0, n, step):
if cb(i):
break
curr = i
for i in range(curr, n):
if cb(i):
if not cr(i):
break
return i
return -1
def test():
test = [(55, 14), (422, 12), (441, 255), (245, 112), (12421441, 244124)]
for n, b in test:
problem = Problem(n, b)
cb = problem.checkBreak
cr = problem.checkResult
res = solve(n, cb, cr)
assert cr(res)
print("Done")
if __name__ == "__main__":
test()
Typescript implement¶
O(sqrt n)
This instead take a different way of input (and testing using jest)
import two_crystal_balls from "@code/TwoCrystalBalls";
test("two crystal balls", function () {
let idx = Math.floor(Math.random() * 10000);
const data = new Array(10000).fill(false);
for (let i = idx; i < 10000; ++i) {
data[i] = true;
}
expect(two_crystal_balls(data)).toEqual(idx);
expect(two_crystal_balls(new Array(821).fill(false))).toEqual(-1);
});
We doing the same thing here:
export default function two_crystal_balls(breaks: boolean[]): number {
let step = Math.floor(Math.sqrt(breaks.length));
let res = -1;
let curr = 0;
for (let i = 0; i < breaks.length; i += step) {
if (breaks[i]) break;
curr = i;
}
for (let i = curr; i < breaks.length; i++) {
if (breaks[i]) {
res = i;
break;
}
}
return res;
}
C implementation¶
O(sqrt n)
I do try using math.h
, but it seem my library can’t even link, so I instead use a implementation for integer sqrt (Newton’s method, Wikipedia version).
Here I use same input as the python version, where we are passed with check break cb
function (that only return True
two time and return -1
to mimic the dropping) and number n
for building height.
#include <stdio.h>
int b = 0;
int n = 0;
int count = 2;
// We return 0 if False, 1 if True, -1 which mean out of ball
// As boolean in C is just 0 for False and 1 for True
int checkBreak(int x){
if (count == 0)
return -1;
count = count - (x >= b);
return x >= b;
}
// Square root of integer
unsigned int intSqrt(unsigned int s)
{
// Zero yields zero
// One yields one
if (s <= 1)
return s;
// Initial estimate (must be too high)
unsigned int x0 = s / 2;
// Update
unsigned int x1 = (x0 + s / x0) / 2;
while (x1 < x0) // Bound check
{
x0 = x1;
x1 = (x0 + s / x0) / 2;
}
return x0;
}
int solve(int n, int (* cb)(int) ) {
int step = intSqrt(n);
int i = 0;
for (i = 0; i < n; i += step) {
if (cb(i)) break;
}
for (i = i - step; i < n; i ++) {
if (cb(i)) break;
}
return i;
}
int main() {
int input_n[] = {53,163,1616,7747};
int input_b[] = {12,13,1416,247};
for (int i = 0; i < 4; i++) {
count = 2;
n = input_n[i];
b = input_b[i];
if (b == solve(n, checkBreak)) {
printf("Test %d pass!\n", i);
}
else {
count = 2;
printf("What, %d != %d\n", b, solve(n, checkBreak));
}
}
return 0;
}
Created : September 3, 2023