-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy patheuler-0012.cpp
129 lines (119 loc) · 4.15 KB
/
euler-0012.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// ////////////////////////////////////////////////////////
// # Title
// Highly divisible triangular number
//
// # URL
// https://projecteuler.net/problem=12
// http://euler.stephan-brumme.com/12/
//
// # Problem
// The sequence of triangle numbers is generated by adding the natural numbers.
// So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28.
// The first ten terms would be:
//
// 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
//
// Let us list the factors of the first seven triangle numbers:
//
// 1: 1
// 3: 1,3
// 6: 1,2,3,6
// 10: 1,2,5,10
// 15: 1,3,5,15
// 21: 1,3,7,21
// 28: 1,2,4,7,14,28
//
// We can see that 28 is the first triangle number to have over five divisors.
//
// What is the value of the first triangle number to have over five hundred divisors?
//
// # Solved by
// Stephan Brumme
// February 2017
//
// # Algorithm
// Similar to other problems, my solution consists of two steps
// 1. precompute all possible inputs
// 2. for each test case: perform a simple lookup
//
// It takes less than a second to find all such numbers with at most 1000 divisors.
// Two "tricks" are responsible to achieve that speed:
// You can get all divisors of `x` by analyzing all potential divisors `i<=sqrt{x}` instead of `i<x`.
// Whenever we find a valid divisor `i` then another divisor `j=frac{x}{y}` exists.
// The only exception is `i=sqrt{x}` because then `j=i`.
//
// Somehow more subtle is my observation that when numbers have more than about 300 divisors,
// the smallest one always end with a zero. I cannot prove that, I just saw it while debugging my code.
//
// I decided to store all my results in a ''std::vector'' called ''smallest'' where
// ''smallest[x]'' contains the smallest triangle number with at least ''x'' divisors.
//
// While filling that container, the program encounters many "gaps":
// e.g. 10 is the smallest number with 4 divisors and 28 is the smallest number with 6 divisors
// but there is no number between 10 and 28 with __5__ divisors.
// Therefore 28 is the smallest number with __at least 5__ divisors, too.
//
// # Alternative
// Prime factorization can find the result probably a bit faster.
#include <iostream>
#include <vector>
int main()
{
// find the smallest number with at least 1000 divisors
// (due to Hackerrank's input range)
const unsigned int MaxDivisors = 1000;
// store [divisors] => [smallest number]
std::vector<unsigned int> smallest;
smallest.push_back(0); // 0 => no divisors
// for index=1 we have triangle=1
// for index=2 we have triangle=3
// for index=3 we have triangle=6
// ...
// for index=7 we have triangle=28
// ...
unsigned int index = 0;
unsigned int triangle = 0; // same as index*(index+1)/2
while (smallest.size() < MaxDivisors)
{
// next triangle number
index++;
triangle += index;
// performance tweak (5x faster):
// I observed that the "best" numbers with more than 300 divisors end with a zero
// that's something I cannot prove right now, I just "saw" that debugging my code
if (smallest.size() > 300 && triangle % 10 != 0)
continue;
// find all divisors i where i*j=triangle
// it's much faster to assume i < j, which means i*i < triangle
// whenever we find i then there is a j, too
unsigned int divisors = 0;
unsigned int i = 1;
while (i*i < triangle)
{
// divisible ? yes, we found i and j, that's two divisors
if (triangle % i == 0)
divisors += 2;
i++;
}
// if i=j then i^2=triangle and we have another divisor
if (i*i == triangle)
divisors++;
// fill gaps:
// e.g. 10 is the smallest number with 4 divisors
// 28 is the smallest number with 6 divisors
// there is no number between 10 and 28 with 5 divisors
// therefore 28 is the smallest number with AT LEAST 5 divisors, too
while (smallest.size() <= divisors)
smallest.push_back(triangle);
}
unsigned int tests;
std::cin >> tests;
while (tests--)
{
unsigned int minDivisors;
std::cin >> minDivisors;
// problem setting asks for "over" x divisors => "plus one"
std::cout << smallest[minDivisors + 1] << std::endl;
}
return 0;
}