solutions.problem18.max_path_sum()   B
last analyzed

Complexity

Conditions 8

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 16
nop 1
dl 0
loc 30
rs 7.3333
c 0
b 0
f 0
1
"""
2
Project Euler Problem 18: Maximum Path Sum I
3
============================================
4
5
.. module:: solutions.problem18
6
   :synopsis: My solution to problem #18.
7
8
The source code for this problem can be
9
`found here <https://bitbucket.org/nekedome/project-euler/src/master/solutions/problem18.py>`_.
10
11
Problem Statement
12
#################
13
14
By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (120/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
15
to bottom is :math:`23`.
16
17
.. math::
18
19
    &&&& \\color{red}{3} &&& \\\\
20
    &&& \\color{red}{7} && 4 && \\\\
21
    && 2 && \\color{red}{4} && 6 & \\\\
22
    & 8 && 5 && \\color{red}{9} && 3
23
24
That is, :math:`3 + 7 + 4 + 9 = 23`.
25
26
Find the maximum total from top to bottom of the triangle below:
27
28
.. math::
29
30
    &&&&&&&&&&&&&&& 75 &&&&&&&&&&&&&& \\\\
31
    &&&&&&&&&&&&&& 95 && 64 &&&&&&&&&&&&& \\\\
32
    &&&&&&&&&&&&& 17 && 47 && 82 &&&&&&&&&&&& \\\\
33
    &&&&&&&&&&&& 18 && 35 && 87 && 10 &&&&&&&&&&& \\\\
34
    &&&&&&&&&&& 20 && 04 && 82 && 47 && 65 &&&&&&&&&& \\\\
35
    &&&&&&&&&& 19 && 01 && 23 && 75 && 03 && 34 &&&&&&&&& \\\\
36
    &&&&&&&&& 88 && 02 && 77 && 73 && 07 && 63 && 67 &&&&&&&& \\\\
37
    &&&&&&&& 99 && 65 && 04 && 28 && 06 && 16 && 70 && 92 &&&&&&& \\\\
38
    &&&&&&& 41 && 41 && 26 && 56 && 83 && 40 && 80 && 70 && 33 &&&&&& \\\\
39
    &&&&&& 41 && 48 && 72 && 33 && 47 && 32 && 37 && 16 && 94 && 29 &&&&& \\\\
40
    &&&&& 53 && 71 && 44 && 65 && 25 && 43 && 91 && 52 && 97 && 51 && 14 &&&& \\\\
41
    &&&& 70 && 11 && 33 && 28 && 77 && 73 && 17 && 78 && 39 && 68 && 17 && 57 &&& \\\\
42
    &&& 91 && 71 && 52 && 38 && 17 && 14 && 91 && 43 && 58 && 50 && 27 && 29 && 48 && \\\\
43
    && 63 && 66 && 04 && 68 && 89 && 53 && 67 && 30 && 73 && 16 && 69 && 87 && 40 && 31 & \\\\
44
    & 04 && 62 && 98 && 27 && 23 && 09 && 70 && 98 && 73 && 93 && 38 && 53 && 60 && 04 && 23
45
46
.. note:: as there are only :math:`16384` routes, it is possible to solve this problem by trying every route. However,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (118/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
47
          :mod:`Problem 67 <solutions.problem67>`, is the same challenge with a triangle containing one-hundred rows; it
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (120/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
48
          cannot be solved by brute force, and requires a clever method! ;o)
49
50
Solution Discussion
51
###################
52
53
This problem can be efficiently solved using a dynamically programming algorithm.
54
55
The algorithm iterates over the rows of the triangle, :math:`t_i`, and builds a list of partial solutions, :math:`p_j`,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (119/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
56
that provide the maximum path sum ending at the :math:`(i,j)^{th}` position in the triangle. This is built one row at a
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (119/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
57
time.
58
59
Upon reaching the final row, :math:`p_j` contains the maximum path sum from the root of the triangle to
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
60
:math:`t_{n - 1,j}` for :math:`j \\in 0, \\dots, n - 1`. The maximum path sum overall is simply then the maximum value
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (118/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
61
in :math:`p`.
62
63
As noted in the problem description, a naive brute-force must consider each of the :math:`2^{15} = 32768` distinct
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (114/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
64
paths. While this would be relatively quick, the algorithm has exponential time complexity in the depth :math:`n` of the
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (120/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
65
triangle. That is, :math:`O(2^{n-1})` where :math:`n` is the number of rows in the triangle.
66
67
.. note:: the problem is wrong, there are actually :math:`2^{15}` paths not :math:`2^{14}` through this triangle.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (113/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
68
69
This dynamic programming algorithm has time complexity :math:`O(1 + 2 + ... + n) = O(n^2)`.
70
71
Solution Implementation
72
#######################
73
74
.. literalinclude:: ../../solutions/problem18.py
75
   :language: python
76
   :lines: 79-
77
"""
78
79
from typing import List
80
81
82
def max_path_sum(triangle: List[List[int]]) -> int:
83
    """ Compute the maximum path sum through the triangle
84
85
    A path moves from the triangle root to a leaf node, with one entry from each row. As the path moves from one row to
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (119/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
86
    the next, it may connect to adjacent entries only.
87
88
    :param triangle: :math:`2`-dimensional list of integers (the triangle)
89
    :return: the maximum path sum through `triangle`
90
    """
91
92
    # Perform some simple validation on triangle
93
    assert isinstance(triangle, list), "triangle must be a triangular 2-dimensional list of integers"
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (101/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
94
    for i, row in enumerate(triangle):
95
        assert len(row) == i + 1, "row {} has length {}, it should have {} to be triangular".format(i, len(row), i + 1)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (119/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
96
        for elt in row:
97
            assert isinstance(elt, int), "triangle entries must be integers"
98
99
    # Build up the answer one row at a time using a dynamic programming algorithm
100
    n = len(triangle)
0 ignored issues
show
Coding Style Naming introduced by
The name n does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
101
    partial_sum_to = [0] * n
102
    for i in range(n):
103
        # After each iteration i, part_sum_to[j] is the maximum path sum ending at triangle[i][j]
104
        temp_partial_sum = partial_sum_to.copy()  # keep an unmodified version of partial_sum from last iteration
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (113/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
105
        temp_partial_sum[0] = partial_sum_to[0] + triangle[i][0]  # only one possible path ends at triangle[i][0]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (113/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
106
        for j in range(1, i):
107
            temp_partial_sum[j] = max(partial_sum_to[j - 1], partial_sum_to[j]) + triangle[i][j]
108
        temp_partial_sum[i] = partial_sum_to[i - 1] + triangle[i][i]  # only one possible path ends at triangle[i][i]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (117/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
109
        partial_sum_to = temp_partial_sum
110
111
    return max(partial_sum_to)  # find the maximum path sum ending at one of the leaf nodes
112
113
114
def solve():
115
    """ Compute the answer to Project Euler's problem #18 """
116
    triangle = [
117
                                    [75],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 28 spaces).
Loading history...
118
                                  [95, 64],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 26 spaces).
Loading history...
119
                                [17, 47, 82],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 24 spaces).
Loading history...
120
                              [18, 35, 87, 10],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 22 spaces).
Loading history...
121
                            [20,  4, 82, 47, 65],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 20 spaces).
Loading history...
Coding Style introduced by
Exactly one space required after comma
Loading history...
122
                          [19,  1, 23, 75,  3, 34],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 18 spaces).
Loading history...
Coding Style introduced by
Exactly one space required after comma
Loading history...
123
                        [88,  2, 77, 73,  7, 63, 67],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 16 spaces).
Loading history...
Coding Style introduced by
Exactly one space required after comma
Loading history...
124
                      [99, 65,  4, 28,  6, 16, 70, 92],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 14 spaces).
Loading history...
Coding Style introduced by
Exactly one space required after comma
Loading history...
125
                    [41, 41, 26, 56, 83, 40, 80, 70, 33],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 12 spaces).
Loading history...
126
                  [41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 10 spaces).
Loading history...
127
                [53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 8 spaces).
Loading history...
128
              [70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 6 spaces).
Loading history...
129
            [91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 4 spaces).
Loading history...
130
          [63, 66,  4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 2 spaces).
Loading history...
Coding Style introduced by
Exactly one space required after comma
Loading history...
131
        [ 4, 62, 98, 27, 23,  9, 70, 98, 73, 93, 38, 53, 60,  4, 23]
0 ignored issues
show
Coding Style introduced by
No space allowed after bracket
Loading history...
Coding Style introduced by
Exactly one space required after comma
Loading history...
132
    ]
133
    return max_path_sum(triangle)
134
135
136
expected_answer = 1074
0 ignored issues
show
Coding Style Naming introduced by
The name expected_answer does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
137