Skip to content

Commit

Permalink
Add docs for 16b that explain the shortcut
Browse files Browse the repository at this point in the history
  • Loading branch information
nlowe committed Dec 17, 2019
1 parent efebafc commit 0c83e4c
Showing 1 changed file with 63 additions and 4 deletions.
67 changes: 63 additions & 4 deletions challenge/day16/b.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func b(challenge *challenge.Input) string {
}

offset := util.MustAtoI(in[:7])
if offset < len(data)/2 {
panic(fmt.Errorf("shortcut won't work for input of length %d (wanted offset %d)", len(data), offset))
}

data = data[offset:]
for i := 0; i < iterations; i++ {
data = shortcut(data)
Expand All @@ -40,9 +44,64 @@ func b(challenge *challenge.Input) string {
return result.String()
}

// TODO: Here be vodo. I need to read up on why this works.
// this was mostly pieced together from posts on the
// subreddit.
// shortcut performs a fast FFT with the assumption that the input is second half of the actual input
//
// Let's assume our input length is 20 and the answer offset is at least at position 10 or later. The matrix formed by
// the phase factors has the following format (split into quadrants for visibility):
//
// 1 0 -1 0 1 0 -1 0 1 0 | -1 0 1 0 -1 0 1 0 -1 0
// 0 1 1 0 0 -1 -1 0 0 1 | 1 0 0 -1 -1 0 0 1 1 0
// 0 0 1 1 1 0 0 0 -1 -1 | -1 0 0 0 1 1 1 0 0 0
// 0 0 0 1 1 1 1 0 0 0 | 0 -1 -1 -1 -1 0 0 0 0 1
// 0 0 0 0 1 1 1 1 1 0 | 0 0 0 0 -1 -1 -1 -1 -1 0
// 0 0 0 0 0 1 1 1 1 1 | 1 0 0 0 0 0 0 -1 -1 -1
// 0 0 0 0 0 0 1 1 1 1 | 1 1 1 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 1 1 1 | 1 1 1 1 1 0 0 0 0 0
// 0 0 0 0 0 0 0 0 1 1 | 1 1 1 1 1 1 1 0 0 0
// 0 0 0 0 0 0 0 0 0 1 | 1 1 1 1 1 1 1 1 1 0
// |
// -------------------------------+--------------------------------
// |
// 0 0 0 0 0 0 0 0 0 0 | 1 1 1 1 1 1 1 1 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 1 1 1 1 1 1 1 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 0 1 1 1 1 1 1 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 0 0 1 1 1 1 1 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 0 0 0 1 1 1 1 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 0 0 0 0 1 1 1 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 1 1 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 1 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 1 1
// 0 0 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 0 1
//
// Remember that row i in the matrix modifies digit i in each iteration. Since our offset is at least within the second
// half we only care about the **bottom** half of the phase transform matrix, as these are the only rows that contribute
// to the result.
//
// Additionally, notice that the only phase factor that we're multiplying by is either 1 or 0 by this point. The FFT
// calculates the value of each digit by multiplying each of the input digits pair-wise by each element from a row of a
// column and then taking the sum of all of those calculations mod10. Since the left side of the bottom half of the
// transform matrix is all zero, none of those digits contribute to the result in any iteration either, so we can
// discard them.
//
// Let's zoom in on the quadrant that we care about:
//
// 1 1 1 1 1 1 1 1 1 1
// 0 1 1 1 1 1 1 1 1 1
// 0 0 1 1 1 1 1 1 1 1
// 0 0 0 1 1 1 1 1 1 1
// 0 0 0 0 1 1 1 1 1 1
// 0 0 0 0 0 1 1 1 1 1
// 0 0 0 0 0 0 1 1 1 1
// 0 0 0 0 0 0 0 1 1 1
// 0 0 0 0 0 0 0 0 1 1
// 0 0 0 0 0 0 0 0 0 1
//
// Recall from earlier that when we see a 0 in this phase transform matrix we can discard that digit from the
// calculation each iteration. Simply put, this means that digit 0 is the sum of all digits mod10, digit 1 is the sum
// of digits 1..n mod10, digit 2 is the sum of digits 2..n mod10, and so on. This means we can quickly calculate the
// value of all digits by first taking the cumulative sum of all digits, calculating the cumulative sum mod10, and
// finally subtracting the initial digit from the cumulative sum (for the next iteration since this digit will no longer
// contribute to the result for the rest of the iteration).
func shortcut(in []int) []int {
cumulativeSum := 0
for _, v := range in {
Expand All @@ -51,7 +110,7 @@ func shortcut(in []int) []int {

var result []int
for i := range in {
result = append(result, ((cumulativeSum%10)+10)%10)
result = append(result, cumulativeSum%10)
cumulativeSum -= in[i]
}

Expand Down

0 comments on commit 0c83e4c

Please sign in to comment.