From 8a1a0b08dfcdbf8a3ea92ecdcede06a146995282 Mon Sep 17 00:00:00 2001 From: Parnic Date: Tue, 21 Jun 2022 01:38:03 -0500 Subject: [PATCH] Add bisect utility This is too common of an optimization to not have this readily accessible. And I kinda like how this worked out, too. Go is fun. Plus this both speeds up and "fixes" day 14's part 2 solution (it was always giving a correct answer, but mostly by chance based on how the input numbers worked out). --- days/14.go | 24 ++++-------------------- utilities/bisect.go | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 utilities/bisect.go diff --git a/days/14.go b/days/14.go index 87b5224..8b82b17 100644 --- a/days/14.go +++ b/days/14.go @@ -104,26 +104,10 @@ func (d *Day14) Part1() string { func (d *Day14) Part2() string { oreAvailable := int64(1000000000000) estimate := oreAvailable / d.getOreRequiredForFuel(1) - - high := estimate * 2 - low := estimate - - lastSuccess := low - lastFailure := high - fuelProduced := low - - for math.Abs(float64(lastFailure-lastSuccess)) > 1 { - oreConsumed := d.getOreRequiredForFuel(fuelProduced) - adjustment := (lastFailure - lastSuccess) / 2 - if oreConsumed < oreAvailable { - lastSuccess = fuelProduced - } else { - lastFailure = fuelProduced - adjustment = -adjustment - } - - fuelProduced += adjustment - } + lastSuccess := u.Bisect(estimate, estimate*2, 1, func(val int64) bool { + oreConsumed := d.getOreRequiredForFuel(val) + return oreConsumed < oreAvailable + }) return fmt.Sprintf("Maximum fuel we can make from 1 trillion ore: %s%d%s", u.TextBold, lastSuccess, u.TextReset) } diff --git a/utilities/bisect.go b/utilities/bisect.go new file mode 100644 index 0000000..d2038ad --- /dev/null +++ b/utilities/bisect.go @@ -0,0 +1,26 @@ +package utilities + +import ( + "math" +) + +// Bisect takes a known-good low and known-bad high value as the bounds +// to bisect, and a function to test each value for success or failure. +// If the function succeeds, the value is adjusted toward the maximum, +// and if the function fails, the value is adjusted toward the minimum. +// The final value is returned when the difference between the success +// and the failure is less than or equal to the acceptance threshold +// (usually 1, for integers). +func Bisect[T Number](low, high, threshold T, tryFunc func(val T) bool) T { + for T(math.Abs(float64(high-low))) > threshold { + currVal := low + ((high - low) / 2) + success := tryFunc(currVal) + if success { + low = currVal + } else { + high = currVal + } + } + + return low +}