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 |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
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. |
|
|
|
|
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 |
|
|
|
|
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)} \\\\ |
|
|
|
|
71
|
|
|
&= d \\bigg(\\frac{n}{2} \\bigg) \\times d(n + 1) & \\; \\; \\; \\mbox{(if n is even)} |
|
|
|
|
72
|
|
|
|
73
|
|
|
So, the overall solution is to build a sieve on the number of divisors in the range :math:`n=1,\\dots,answer` |
|
|
|
|
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 |
|
|
|
|
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): |
|
|
|
|
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 |
|
|
|
|
125
|
|
|
|
This check looks for lines that are too long. You can specify the maximum line length.