From b97ec2eb1752ddfcb5feac467b4f4ba4b83655ff Mon Sep 17 00:00:00 2001 From: Parnic Date: Tue, 21 Jun 2022 01:17:31 -0500 Subject: [PATCH 1/2] 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. --- days/14.go | 24 ++++-------------------- utilities/bisect.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 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..0bd62b1 --- /dev/null +++ b/utilities/bisect.go @@ -0,0 +1,28 @@ +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 { + currVal := low + + for T(math.Abs(float64(high-low))) > threshold { + currVal = low + ((high - low) / 2) + success := tryFunc(currVal) + if success { + low = currVal + } else { + high = currVal + } + } + + return currVal +} From e153d022a7a3febf4e5de6edef803b794ad77b06 Mon Sep 17 00:00:00 2001 From: Parnic Date: Tue, 21 Jun 2022 00:21:58 -0500 Subject: [PATCH 2/2] Intcode Reset optimization Initially I noticed that I was copying twice unnecessarily (once in init() after nulling out memory, and again after returning from init()). After cleaning that up, I realized that we don't need to create a new buffer at all if the program never malloc-ed, so sometimes we can skip the re-init and we can always avoid the double-copy. I don't know if this is actually measurable anywhere, but I spot-checked some results and I still seem to be getting the same answers, so I'm gonna roll with it. --- utilities/intcode.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/utilities/intcode.go b/utilities/intcode.go index f0bf4b4..d037711 100644 --- a/utilities/intcode.go +++ b/utilities/intcode.go @@ -128,9 +128,15 @@ func (p *IntcodeProgram) ensureMemoryCapacity(address int) { } func (p *IntcodeProgram) Reset() { - p.memory = nil + wiped := false + if len(p.memory) != len(p.program) { + wiped = true + p.memory = nil + } p.init() - copy(p.memory, p.program) + if !wiped { + copy(p.memory, p.program) + } p.relativeBase = 0 }