diff --git a/13input.txt b/13input.txt new file mode 100644 index 0000000..0a1f478 --- /dev/null +++ b/13input.txt @@ -0,0 +1,2 @@ +1000299 +41,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,37,x,x,x,x,x,971,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,17,13,x,x,x,x,23,x,x,x,x,x,29,x,487,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,19 \ No newline at end of file diff --git a/2020.csproj b/2020.csproj index 953c3e8..99f0fef 100644 --- a/2020.csproj +++ b/2020.csproj @@ -47,6 +47,9 @@ PreserveNewest + + PreserveNewest + diff --git a/Program.cs b/Program.cs index dc64140..6394332 100644 --- a/Program.cs +++ b/Program.cs @@ -17,6 +17,7 @@ Q10.Go(); Q11.Go(); Q12.Go(); + Q13.Go(); Util.Log($"Total time={(System.DateTime.Now - start).TotalMilliseconds}ms"); } } diff --git a/Q13.cs b/Q13.cs new file mode 100644 index 0000000..402ece0 --- /dev/null +++ b/Q13.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace _2020 +{ + class Q13 + { + static int leaveTime; + static List busses = new List(); + + public static void Go() + { + var start = DateTime.Now; + MakeList(); + Util.Log($"Q13 MakeList took {(DateTime.Now - start).TotalMilliseconds}ms"); + var p1start = DateTime.Now; + Part1(); + Util.Log($"Q13 part1 took {(DateTime.Now - p1start).TotalMilliseconds}ms"); + var p2start = DateTime.Now; + Part2(); + Util.Log($"Q13 part2 took {(DateTime.Now - p2start).TotalMilliseconds}ms"); + + Util.Log($"Q13 took {(DateTime.Now - start).TotalMilliseconds}ms"); + } + + static void MakeList() + { + var lines = File.ReadAllLines("13input.txt"); + if (lines.Length != 2) + { + throw new Exception("Invalid input"); + } + + leaveTime = Convert.ToInt32(lines[0]); + foreach (var bus in lines[1].Split(',')) + { + if (bus == "x") + { + busses.Add(-1); + continue; + } + + busses.Add(Convert.ToInt32(bus)); + } + } + + static void Part1() + { + int smallestModulo = int.MaxValue; + int smallestBus = -1; + foreach (var bus in busses) + { + if (bus < 0) + { + continue; + } + + var mod = bus - (leaveTime % bus); + if (mod < smallestModulo) + { + smallestModulo = mod; + smallestBus = bus; + } + } + + Util.Log($"Q13Part1: soonest bus={smallestBus}, departs={leaveTime + smallestModulo}, wait time={smallestModulo}m, answer={smallestBus * smallestModulo}"); + } + + // taken from https://rosettacode.org/wiki/Chinese_remainder_theorem#C.23, modified for int64s. i don't know how this works. who am i, sun-tzu? + static long GetChineseRemainderTheorem(IEnumerable n, IEnumerable a) + { + long prod = n.Aggregate(1L, (i, j) => i * j); + long p; + long sm = 0; + for (int i = 0; i < n.Count(); i++) + { + p = prod / n.ElementAt(i); + sm += a.ElementAt(i) * ModularMultiplicativeInverse(p, n.ElementAt(i)) * p; + } + return sm % prod; + } + + static long ModularMultiplicativeInverse(long a, long mod) + { + long b = a % mod; + for (long x = 1; x < mod; x++) + { + if ((b * x) % mod == 1) + { + return x; + } + } + return 1; + } + + static void Part2() + { + var a = new List(); + var n = new List(); + for (int i = 0; i < busses.Count; i++) + { + if (busses[i] <= 0) + { + continue; + } + + a.Add(busses[i] - i % busses[i]); + n.Add(busses[i]); + } + + Util.Log($"Q13Part2: val={GetChineseRemainderTheorem(n, a)}"); + } + + // this is how i initially solved it. took ~30mins. i'm not proud of it, but the "real" answer apparently required specialized knowledge i did not have. + static void Part2BruteForce() + { + int root = busses.First(x => x > 0); + var sorted = busses.OrderBy(x => x); + var largest = sorted.Last(); + + long checkVal = 0; + + var idx = busses.IndexOf(largest); + int answersFound = 0; + int numThreads = 13; + Parallel.For(0, numThreads, thread => + { + var threadVal = checkVal + (thread * largest); + long rootCheckVal = 0; + while (answersFound == 0) + { + threadVal += numThreads * largest; + rootCheckVal = threadVal - idx; + if (rootCheckVal % root == 0) + { + var gotit = false; + for (int i = 1; i < busses.Count; i++) + { + if (busses[i] <= 0) + { + continue; + } + + if ((rootCheckVal + i) % busses[i] == 0) + { + gotit = true; + } + else + { + gotit = false; + break; + } + } + + if (gotit) + { + Interlocked.Increment(ref answersFound); + break; + } + } + } + + Util.Log($"Q13Part2: val={rootCheckVal}"); + }); + } + } +}