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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
71 | &= d \\bigg(\\frac{n}{2} \\bigg) \\times d(n + 1) & \\; \\; \\; \\mbox{(if n is even)} |
||
0 ignored issues
–
show
|
|||
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
|
|||
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
|
|||
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
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. ![]() |
|||
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
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. ![]() |
|||
125 |
This check looks for lines that are too long. You can specify the maximum line length.