Use math instead of binary search

This commit is contained in:
2021-12-18 17:21:43 +01:00
parent 9c299f140c
commit cc81a7012b

View File

@@ -19,9 +19,13 @@ fn solve_quadratic(a: f64, b: f64, c: f64) -> Option<f64> {
None None
} else { } else {
// Don't care about the smaller solution due to problem statement // Don't care about the smaller solution due to problem statement
if a > 0. {
Some((-b + d.sqrt()) / 2. / a)
} else {
Some((-b - d.sqrt()) / 2. / a) Some((-b - d.sqrt()) / 2. / a)
} }
} }
}
fn position(initial: i32, time: i32) -> i32 { fn position(initial: i32, time: i32) -> i32 {
time * (2 * initial - time + 1) / 2 time * (2 * initial - time + 1) / 2
@@ -60,37 +64,27 @@ fn find_hit(initial: i32, range: &RangeInclusive<i32>) -> Option<RangeInclusive<
} }
fn find_speed(x: i32, range: &RangeInclusive<i32>) -> Option<RangeInclusive<i32>> { fn find_speed(x: i32, range: &RangeInclusive<i32>) -> Option<RangeInclusive<i32>> {
let mut min = 0; if *range.end() <= position(x, x) {
let mut max = *range.end(); // Can and should come to a full stop
let max = solve_quadratic(0.5, 0.5, -*range.end() as f64)? as i32;
// Need to tweak the formula as x slows down let min = (0..=max)
let x_pos = |speed| position(speed, speed.min(x));
while max >= min {
let speed = (max + min) / 2;
let pos = x_pos(speed);
if range.contains(&x_pos(speed)) {
let min_speed = (0..speed)
.rev() .rev()
.take_while(|&speed| range.contains(&x_pos(speed))) .take_while(|&n| range.contains(&position(n, n)))
.min() .last()?;
.unwrap_or(speed);
let max_speed = ((speed + 1)..) Some(min..=max)
.take_while(|&speed| range.contains(&x_pos(speed)))
.max()
.unwrap_or(speed);
return Some(min_speed..=max_speed);
} else if pos < *range.start() {
min = speed + 1;
} else { } else {
max = speed - 1; // Might hit the target at speed
} let max = (x * x + 2 * *range.end() - x) / (2 * x);
}
None let min = (0..=max)
.rev()
.take_while(|&n| range.contains(&position(n, n.min(x))))
.last()?;
Some(min..=max)
}
} }
fn parse_range(input: &[u8]) -> IResult<&[u8], RangeInclusive<i32>> { fn parse_range(input: &[u8]) -> IResult<&[u8], RangeInclusive<i32>> {