|
| 1 | +This one reduces to basically solving two linear equations, but it's kind of |
| 2 | +fun to see what the *linear* haskell library gives us to make things more |
| 3 | +convenient. |
| 4 | + |
| 5 | +Basically for `xa`, `ya`, `xb`, `yb`, we want to solve the matrix equation `M p |
| 6 | += c` for `p`, where `c` is our target `<x, y>`, and `M` is `[ xa xb; ya yb ]`. We're |
| 7 | +going to assume that our two buttons are linearly independent (they are not |
| 8 | +multiples of each other). Note that the `M` matrix is the transpose of the |
| 9 | +numbers as we originally parse them. |
| 10 | + |
| 11 | +Normally we can solve this as `p = M^-1 C`, where `M^-1 = [ yb -xb; -ya xa] / |
| 12 | +(ad - bc)`. However, we only care about integer solutions. This means that we |
| 13 | +can do some checks: |
| 14 | + |
| 15 | +1. Compute `det = ad - bc` and a matrix `U = [yb -xb ; -ya xa]`, which is |
| 16 | + `M^-1 * det`. |
| 17 | +2. Compute `p*det = U c` |
| 18 | +3. Check that `det` is not 0 |
| 19 | +4. Check that ``(`mod` det)`` is 0 for all items in `U c` |
| 20 | +5. Our result is then the ``(`div` det)`` for all items in `U c`. |
| 21 | + |
| 22 | +*linear* has the `det22` method for the determinant of a 2x2 matrix, but it |
| 23 | +doesn't quite have the `M^-1 * det` function, it only has `M^-1` for |
| 24 | +`Fractional` instances. So we can write our own: |
| 25 | + |
| 26 | +```haskell |
| 27 | +-- | Returns det(A) and inv(A)det(A) |
| 28 | +inv22Int :: (Num a, Eq a) => M22 a -> Maybe (a, M22 a) |
| 29 | +inv22Int m@(V2 (V2 a b) (V2 c d)) |
| 30 | + | det == 0 = Nothing |
| 31 | + | otherwise = Just (det, V2 (V2 d (-b)) (V2 (-c) a)) |
| 32 | + where |
| 33 | + det = det22 m |
| 34 | + |
| 35 | +type Point = V2 Int |
| 36 | + |
| 37 | +getPrize :: V2 Point -> Point -> Maybe Int |
| 38 | +getPrize coeff targ = do |
| 39 | + (det, invTimesDet) <- inv22Int (transpose coeff) |
| 40 | + let resTimesDet = invTimesDet !* targ |
| 41 | + V2 a b = (`div` det) <$> resTimesDet |
| 42 | + guard $ all ((== 0) . (`mod` det)) resTimesDet |
| 43 | + pure $ 3 * a + b |
| 44 | + |
| 45 | +part1 :: [(V2 Point, Point)] -> Int |
| 46 | +part1 = sum . mapMaybe (uncurry getPrize) |
| 47 | + |
| 48 | +part2 :: [(V2 Point, Point)] -> Int |
| 49 | +part2 = part2 . map (second (10000000000000 +)) |
| 50 | +``` |
| 51 | + |
| 52 | +Here we take advantage of `transpose`, `det22`, `!*` for matrix-vector |
| 53 | +multiplication, the `Functor` instance of vectors for `<$>`, the `Foldable` |
| 54 | +instance of vectors for `all`, and the `Num` instance of vectors for numeric |
| 55 | +literals and `+`. |
0 commit comments