Euler Problem 11

In the 20×20 grid below, four numbers along a diagonal line have been marked in red.

08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48

The product of these numbers is $26 × 63 × 78 × 14 = 1788696$.

What is the greatest product of four adjacent numbers in the same direction (up, down, left, right, or diagonally) in the 20×20 grid?

We start by parsing the array into three lists of lists (horizontal, diagonal, vertical).

97, 17, 79, 11, 89, 44, 38, 94, 78, 0, 62, 54, 58, 4, 27, 53, 36, 1, 1, 1]

In [1]:
s = """
08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48
"""

def get_index(xs, index, default):
    if index < 0:
        return default
    try:
        return xs[index]
    except IndexError:
        return default

hss = [list(map(int, h.split())) for h in s.split('\n') if h]
vss = [list(vs) for vs in zip(*hss)]
# diagonal from top left to bottom right
dss = [[get_index(hs, -19 + i + j, 1) for j, hs in enumerate(hss)] for i in range(39)]

I really love the way we retrieve the vertical list. (We need the ugly conversion to list because otherwise we cannot concatenate them.) It is at the limit of my mental capabilities to understand why this works. But actually it is quite straight forward. If we provide two lists, zip creates a two-tuple for each field in the lists. If we provide n lists, zip creates a n-tuple for each field in the lists. Easy.

Now we create big list of lists and search each one for the greates four product and then get the maximum. Straight forward. We borrow the find product function from problem 8 because I like it.

In [2]:
xss = hss + vss + dss

def product(xs):
    from operator import mul
    from functools import reduce
    return reduce(mul, xs, 1)

def get_largest_product_of_n_digits(xs, n):
    return max([product(xs[i:i + n]) for i in range(len(xs))])

s = max([get_largest_product_of_n_digits(xs, 4) for xs in xss])
assert(s != 70600674)

Okay, I am actually really dumb. I forgot about the other diagonal. I think I have made the same mistake back when I solved this for the first time. So let's get the missing dss and get the solution.

In [3]:
# diagonal from bottom left to top right
hss.reverse()
dss = [[get_index(hs, -19 + i + j, 1) for j, hs in enumerate(hss)] for i in range(39)]

s = max([get_largest_product_of_n_digits(ds, 4) for ds in dss])
assert(s == 70600674)
print(s)
70600674