adventofcode/2020/day13.go

123 lines
2.4 KiB
Go
Raw Normal View History

package main
import (
"fmt"
"log"
"math"
"math/big"
"os"
"strconv"
"strings"
2020-12-14 23:27:46 +00:00
"git.annabunch.es/annabunches/adventofcode/2020/lib/util"
)
//
// Begin code borrowed from https://golang.hotexamples.com/examples/math.big/Int/GCD/golang-int-gcd-method-examples.html
//
func crt(a, n []*big.Int) (*big.Int, error) {
one := new(big.Int).SetInt64(1)
p := new(big.Int).Set(n[0])
for _, n1 := range n[1:] {
p.Mul(p, n1)
}
var x, q, s, z big.Int
for i, n1 := range n {
q.Div(p, n1)
z.GCD(nil, &s, n1, &q)
if z.Cmp(one) != 0 {
return nil, fmt.Errorf("%d not coprime", n1)
}
x.Add(&x, s.Mul(a[i], s.Mul(&s, &q)))
}
return x.Mod(&x, p), nil
}
//
// End borrowed code
//
func parseInput(input []string) (int, []int) {
earliest, err := strconv.Atoi(input[0])
if err != nil {
log.Panicf(err.Error())
}
busList := make([]int, 0)
for _, value := range strings.Split(input[1], ",") {
if value == "x" {
busList = append(busList, -1)
continue
}
x, err := strconv.Atoi(value)
if err != nil {
log.Panicf(err.Error())
}
busList = append(busList, x)
}
return earliest, busList
}
func findBus(busList []int, earliest int) (int, int) {
bestBus := -1
bestTime := -1
for _, bus := range busList {
if bus == -1 {
continue
}
time := int(math.Ceil(float64(earliest)/float64(bus))) * bus
if bestBus == -1 || time < bestTime {
bestBus = bus
bestTime = time
}
}
return bestBus, bestTime
}
// This uses the Chinese Remainder Theorem to calculate the answer.
// I don't actually understand the underlying logic, I just found this
// while looking around for modulus-related algorithms.
func findTimestampWithCRT(busList []int) *big.Int {
bigBusList := make([]*big.Int, 0)
offsetList := make([]*big.Int, 0)
for i, bus := range busList {
if bus == -1 {
continue
}
bigBus := big.NewInt(int64(bus))
bigBusList = append(bigBusList, bigBus)
offset := big.NewInt(int64(i))
offset.Sub(bigBus, offset)
offsetList = append(offsetList, offset)
}
fmt.Println(offsetList)
fmt.Println(bigBusList)
value, err := crt(offsetList, bigBusList)
if err != nil {
log.Panicf(err.Error())
}
return value
}
func main() {
step := os.Args[1]
2020-12-14 23:27:46 +00:00
values := util.InputParserStrings(os.Args[2])
earliest, busList := parseInput(values)
switch step {
case "1":
busId, departureTime := findBus(busList, earliest)
fmt.Println(busId * (departureTime - earliest))
case "2":
fmt.Println(findTimestampWithCRT(busList))
}
}