solutions.problem12   A
last analyzed

Complexity

Total Complexity 5

Size/Duplication

Total Lines 125
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 125
rs 10
c 0
b 0
f 0
wmc 5

1 Function

Rating   Name   Duplication   Size   Complexity  
A solve() 0 22 5
1
"""
2
Project Euler Problem 12: Highly Divisible Triangular Number
3
============================================================
4
5
.. module:: solutions.problem12
6
   :synopsis: My solution to problem #12.
7
8
The source code for this problem can be
9
`found here <https://bitbucket.org/nekedome/project-euler/src/master/solutions/problem12.py>`_.
10
11
Problem Statement
12
#################
13
14
The sequence of triangle numbers is generated by adding the natural numbers. So the :math:`7^{th}` triangle number would
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
be
16
    :math:`1+2+3+4+5+6+7 = 28`.
17
18
The first ten terms would be:
19
    :math:`1,3,6,10,15,21,28,36,45,55,\\dots`
20
21
Let us list the factors of the first seven triangle numbers:
22
23
.. math::
24
25
     1&: 1 \\\\
26
     3&: 1,3 \\\\
27
     6&: 1,2,3,6 \\\\
28
    10&: 1,2,5,10 \\\\
29
    15&: 1,3,5,15 \\\\
30
    21&: 1,3,7,21 \\\\
31
    28&: 1,2,4,7,14,28
32
33
We can see that :math:`28` is the first triangle number to have over five divisors.
34
35
What is the value of the first triangle number to have over five hundred divisors?
36
37
Solution Discussion
38
###################
39
40
This solution uses a sieve to incrementally build up the count of divisors :math:`d(x)` for all :math:`x` up to some
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (116/100).

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

Loading history...
41
pre-determined upper-bound. With this sieve, the problem is easily solvable.
42
43
All triangle numbers are of the form:
44
    :math:`T_n = 1 + 2 + \\dots + n`
45
which can be re-expressed in the closed form:
46
    :math:`T_n = \\frac{n \\times (n + 1)}{2}`
47
48
Importantly, this expression can be de-composed into pair-wise co-prime components.
49
50
Case (:math:`n` is even):
51
    :math:`T_n = \\frac{n}{2} \\times (n + 1)`
52
53
Case (:math:`n` is odd):
54
    :math:`T_n = n \\times (\\frac{n + 1}{2})`
55
56
It should be easy to see that only one term in each case is divisible by :math:`2`, so prior to dividing out by
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/100).

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

Loading history...
57
:math:`2` we have:
58
59
* (:math:`n` is even) n and (n + 1)
60
* (:math:`n` is odd)  n and (n + 1)
61
62
Clearly, :math:`n` and :math:`(n + 1)` are co-prime, therefore, the two terms in each case are pair-wise co-prime.
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...
63
64
Since these terms are co-prime, the number of divisors in :math:`T_n` can be determined by the number of divisors in
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (116/100).

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

Loading history...
65
each term.
66
67
.. math::
68
69
    d(T_n) &= d \\bigg(n \\times \\frac{n + 1}{2} \\bigg) & \\\\
70
           &= d(n) \\times d \\bigg(\\frac{n + 1}{2} \\bigg)     & \\; \\; \\; \\mbox{(if n is odd)} \\\\
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (105/100).

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

Loading history...
71
           &= d \\bigg(\\frac{n}{2} \\bigg) \\times d(n + 1)       & \\; \\; \\; \\mbox{(if n is even)}
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...
72
73
So, the overall solution is to build a sieve on the number of divisors in the range :math:`n=1,\\dots,answer`
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (109/100).

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

Loading history...
74
75
Finally, we may set a reasonable upper-bound on :math:`answer` by applying the following:
76
77
.. math::
78
79
    & d(answer) \\lt 2 \\times \\sqrt{answer} \\mbox{ and } d(answer) \\gt 500 \\\\
80
    & \\Rightarrow 500 \\lt d(answer) \\lt 2 \\times \\sqrt{answer} \\\\
81
    & \\Rightarrow \\frac{500}{2} \\lt \\sqrt{answer} \\\\
82
    & \\Rightarrow 250 \\lt \\sqrt{answer} \\\\
83
    & \\Rightarrow \\sqrt{answer} \\gt 250 \\\\
84
    & \\Rightarrow answer \\gt 250^2
85
86
While this range will be sufficient, it may be excessive. This algorithm computes the solution quickly enough so I won't
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...
87
investigate a tighter bound on :math:`n`.
88
89
Solution Implementation
90
#######################
91
92
.. literalinclude:: ../../solutions/problem12.py
93
   :language: python
94
   :lines: 97-
95
"""
96
97
from lib.numbertheory import divisors_sieve, is_even
98
99
100
def solve():
101
    """ Compute the answer to Project Euler's problem #12 """
102
103
    target = 500  # target number of divisors
104
    limit = (target // 2) ** 2  # search limit for the sieve
105
106
    # Build the number of divisors sieve
107
    divisors = divisors_sieve(limit, proper=False, aggregate="count")
108
    sieve = {n + 1: divisors_count for n, divisors_count in enumerate(divisors)}
109
110
    # Search through the sieve for the first T_n exceeding the targeted number of divisors
111
    answer = None
112
    for n in range(1, limit - 1):
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...
113
        if is_even(n):
114
            n_divisors = sieve[n // 2] * sieve[n + 1]
115
        else:
116
            n_divisors = sieve[n] * sieve[(n + 1) // 2]
117
        if n_divisors > target:
118
            answer = n * (n + 1) // 2
119
            break
120
121
    return answer
122
123
124
expected_answer = 76576500
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...
125