Skip to content

Commit 76da98e

Browse files
committed
P108, P109: Added Python and Mathematica solutions.
P109, P119: Added Python solutions, tweaked Java solutions for clarity. Readme: Updated solution count.
1 parent ca0c840 commit 76da98e

9 files changed

+219
-24
lines changed

Readme.markdown

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mathematical explanation/proof in the source code to justify the program logic.
99

1010
All problems from #1 to #100 have a Java program, and #1 to #50 have Python and Mathematica programs.
1111
The project codebase contains at least 185 solutions in Java, at least 135 in Python,
12-
at least 115 in Mathematica, and at least 65 in Haskell.
12+
at least 120 in Mathematica, and at least 65 in Haskell.
1313

1414
Java solutions require JDK 5 or above. Python solutions are tested to work on CPython 2.7.10 and 3.4.3. Mathematica solutions are tested to work on Mathematica 5.1.
1515

java/p109.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@ public static void main(String[] args) {
1919

2020

2121
public String run() {
22-
// Initialization
23-
points = new ArrayList<Integer>(); // Orderless, but duplicates are important
22+
// Both lists are orderless but duplicates are important; they are sort of like multisets
23+
points = new ArrayList<Integer>();
2424
for (int i = 1; i <= 20; i++) {
25-
points.add(i * 1);
26-
points.add(i * 2);
27-
points.add(i * 3);
25+
for (int j = 1; j <= 3; j++)
26+
points.add(i * j);
2827
}
2928
points.add(25);
3029
points.add(50);
3130

32-
doublePoints = new ArrayList<Integer>(); // Orderless, but duplicates are important
31+
List<Integer> doublePoints = new ArrayList<Integer>(); // Orderless
3332
for (int i = 1; i <= 20; i++)
3433
doublePoints.add(i * 2);
3534
doublePoints.add(25 * 2);
3635

36+
// Memoization array
3737
ways = new int[3][101][points.size()];
3838
for (int[][] x : ways) {
3939
for (int[] y : x)
@@ -54,7 +54,6 @@ public String run() {
5454

5555

5656
private List<Integer> points;
57-
private List<Integer> doublePoints;
5857

5958
private int[][][] ways;
6059

java/p119.java

+11-16
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
import java.math.BigInteger;
1010
import java.util.ArrayList;
11-
import java.util.Collections;
12-
import java.util.List;
11+
import java.util.SortedSet;
12+
import java.util.TreeSet;
1313

1414

1515
public final class p119 implements EulerSolution {
@@ -33,23 +33,18 @@ public static void main(String[] args) {
3333

3434
public String run() {
3535
for (BigInteger limit = BigInteger.ONE; ; limit = limit.shiftLeft(8)) {
36-
List<BigInteger> candidates = new ArrayList<BigInteger>();
37-
for (int k = 2; BigInteger.valueOf(2).pow(k).compareTo(limit) < 0; k++) {
38-
BigInteger n = BigInteger.valueOf(2);
39-
while (true) {
40-
BigInteger pow = n.pow(k);
41-
if (pow.compareTo(limit) >= 0 && BigInteger.valueOf(pow.toString().length() * 9).compareTo(n) < 0)
36+
SortedSet<BigInteger> candidates = new TreeSet<BigInteger>();
37+
for (int k = 2; BigInteger.valueOf(1).shiftLeft(k).compareTo(limit) < 0; k++) {
38+
for (int n = 2; ; n++) {
39+
BigInteger pow = BigInteger.valueOf(n).pow(k);
40+
if (pow.compareTo(limit) >= 0 && pow.toString().length() * 9 < n)
4241
break;
43-
44-
if (pow.compareTo(BigInteger.TEN) >= 0 && !candidates.contains(pow) && isDigitSumPower(pow))
42+
if (pow.compareTo(BigInteger.TEN) >= 0 && isDigitSumPower(pow))
4543
candidates.add(pow);
46-
n = n.add(BigInteger.ONE);
4744
}
4845
}
49-
if (candidates.size() >= INDEX) {
50-
Collections.sort(candidates);
51-
return candidates.get(INDEX - 1).toString();
52-
}
46+
if (candidates.size() >= INDEX)
47+
return new ArrayList<BigInteger>(candidates).get(INDEX - 1).toString();
5348
}
5449
}
5550

@@ -64,7 +59,7 @@ private static boolean isDigitSumPower(BigInteger x) {
6459
BigInteger pow = base;
6560
while (pow.compareTo(x) < 0)
6661
pow = pow.multiply(base);
67-
return pow.compareTo(x) == 0;
62+
return pow.equals(x);
6863
}
6964

7065

mathematica/p108.mathematica

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
(*
2+
* Solution to Project Euler problem 108
3+
* by Project Nayuki
4+
*
5+
* https://www.nayuki.io/page/project-euler-solutions
6+
* https://github.com/nayuki/Project-Euler-solutions
7+
*)
8+
9+
10+
(*
11+
* Rewrite the equation with x = n+i, y = n+j, and manipulate it:
12+
* 1/n = 1/x + 1/y
13+
* = 1/(n+i) + 1/(n+j)
14+
* = (2n+i+j) / ((n+i)(n+j)).
15+
* n(2n+i+j) = (n+i)(n+j).
16+
* 2n^2 + ni + nj = n^2 + ni + nj + ij.
17+
* n^2 = ij.
18+
* Hence i and j are divisors of n^2. To ensure unique solutions,
19+
* we impose that x <= y, so i <= j. Also, i > 0, otherwise no j exists.
20+
* We have i <= j = n^2 / i, thus i^2 <= n^2. With i being positive, we get that i <= n.
21+
* Therefore the number of solutions for i is the number of divisors of n^2 in the range [1, n].
22+
* n^2 always has an odd number of divisors. One of them is n. As for the remainder of them, half of them are below n
23+
* and half of them are above n. So if n^2 has m divisors, then we want (m+1)/2 of them as solutions for i.
24+
*)
25+
26+
For[n = 1, (DivisorSigma[0, n^2] + 1) / 2 <= 1000, n++]
27+
n

mathematica/p109.mathematica

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
(*
2+
* Solution to Project Euler problem 109
3+
* by Project Nayuki
4+
*
5+
* https://www.nayuki.io/page/project-euler-solutions
6+
* https://github.com/nayuki/Project-Euler-solutions
7+
*)
8+
9+
10+
(* Both lists are orderless but duplicates are important; they are sort of like multisets *)
11+
points = Join[Flatten[Table[i * j, {i, 20}, {j, 3}]], {25, 50}];
12+
doublePoints = Join[Table[i * 2, {i, 20}], {25 * 2}];
13+
14+
Ways[throws_, total_, maxIndex_] := Ways[throws, total, maxIndex] = (* Memoization *)
15+
If[throws == 0,
16+
Boole[total == 0],
17+
If[maxIndex > 1, Ways[throws, total, maxIndex - 1], 0] +
18+
If[points[[maxIndex]] <= total, Ways[throws - 1, total - points[[maxIndex]], maxIndex], 0]]
19+
20+
Sum[
21+
If[doublePoints[[i]] <= remainingPoints,
22+
Ways[throws, remainingPoints - doublePoints[[i]], Length[points]],
23+
0],
24+
{remainingPoints, 99},
25+
{throws, 0, 2},
26+
{i, Length[doublePoints]}]

python/eulertest.py

+3
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,15 @@ def main():
111111
101: "37076114526",
112112
102: "228",
113113
104: "329468",
114+
108: "180180",
115+
109: "38182",
114116
112: "1587000",
115117
113: "51161058134250",
116118
114: "16475640049",
117119
115: "168",
118120
116: "20492570929",
119121
117: "100808458960497",
122+
119: "248155780267521",
120123
120: "333082500",
121124
123: "21035",
122125
124: "21417",

python/p108.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#
2+
# Solution to Project Euler problem 108
3+
# by Project Nayuki
4+
#
5+
# https://www.nayuki.io/page/project-euler-solutions
6+
# https://github.com/nayuki/Project-Euler-solutions
7+
#
8+
9+
import eulerlib, itertools
10+
11+
12+
# Rewrite the equation with x = n+i, y = n+j, and manipulate it:
13+
# 1/n = 1/x + 1/y
14+
# = 1/(n+i) + 1/(n+j)
15+
# = (2n+i+j) / ((n+i)(n+j)).
16+
# n(2n+i+j) = (n+i)(n+j).
17+
# 2n^2 + ni + nj = n^2 + ni + nj + ij.
18+
# n^2 = ij.
19+
# Hence i and j are divisors of n^2. To ensure unique solutions,
20+
# we impose that x <= y, so i <= j. Also, i > 0, otherwise no j exists.
21+
# We have i <= j = n^2 / i, thus i^2 <= n^2. With i being positive, we get that i <= n.
22+
# Therefore the number of solutions for i is the number of divisors of n^2 in the range [1, n].
23+
# n^2 always has an odd number of divisors. One of them is n. As for the remainder of them, half of them are below n
24+
# and half of them are above n. So if n^2 has m divisors, then we want (m+1)/2 of them as solutions for i.
25+
def compute():
26+
for n in itertools.count(1):
27+
if (count_divisors_squared(n) + 1) // 2 > 1000:
28+
return str(n)
29+
30+
31+
# Returns the number of divisors of n^2
32+
def count_divisors_squared(n):
33+
count = 1
34+
end = eulerlib.sqrt(n)
35+
for i in itertools.count(2):
36+
if i > end:
37+
break
38+
if n % i == 0:
39+
j = 0
40+
while True:
41+
n //= i
42+
j += 1
43+
if n % i != 0:
44+
break
45+
count *= j * 2 + 1
46+
end = eulerlib.sqrt(n)
47+
if n != 1: # Remaining largest prime factor
48+
count *= 3
49+
return count
50+
51+
52+
if __name__ == "__main__":
53+
print(compute())

python/p109.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#
2+
# Solution to Project Euler problem 109
3+
# by Project Nayuki
4+
#
5+
# https://www.nayuki.io/page/project-euler-solutions
6+
# https://github.com/nayuki/Project-Euler-solutions
7+
#
8+
9+
10+
def compute():
11+
# Both lists are orderless but duplicates are important; they are sort of like multisets
12+
points = [i * j for i in range(1, 21) for j in range(1, 4)] + [25, 50]
13+
doublepoints = [i * 2 for i in range(1, 21)] + [25 * 2]
14+
15+
# Memoization array, with dimensions (3, 101, len(points))
16+
ways = [[[None] * len(points) for j in range(101)] for i in range(3)]
17+
18+
# Number of ways to get exactly 'total' points in exactly 'throwz' throws, using
19+
# items (unordered) from the 'points' list with index less than or equal to 'maxIndex'.
20+
def calc_ways(throws, total, maxindex):
21+
if ways[throws][total][maxindex] is None:
22+
if throws == 0:
23+
result = 1 if total == 0 else 0
24+
else:
25+
result = 0
26+
if maxindex > 0:
27+
result += calc_ways(throws, total, maxindex - 1)
28+
if points[maxindex] <= total:
29+
result += calc_ways(throws - 1, total - points[maxindex], maxindex)
30+
ways[throws][total][maxindex] = result
31+
return ways[throws][total][maxindex]
32+
33+
checkouts = 0
34+
for remainingpoints in range(1, 100):
35+
for throws in range(0, 3):
36+
for p in doublepoints:
37+
if p <= remainingpoints:
38+
checkouts += calc_ways(throws, remainingpoints - p, len(points) - 1)
39+
return str(checkouts)
40+
41+
42+
if __name__ == "__main__":
43+
print(compute())

python/p119.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#
2+
# Solution to Project Euler problem 119
3+
# by Project Nayuki
4+
#
5+
# https://www.nayuki.io/page/project-euler-solutions
6+
# https://github.com/nayuki/Project-Euler-solutions
7+
#
8+
9+
import itertools
10+
11+
12+
# Candidates have the form n^k, where n >= 2, k >= 2, n^k >= 10, and isDigitSumPower(n^k) == true.
13+
# We also impose n^k < limit. If there are at least 30 candidates under 'limit',
14+
# then the 30th smallest candidate is the answer. Otherwise we raise the limit and search again.
15+
#
16+
# We only need to try the exponents k until 2^k exceeds the limit.
17+
# We only need to try the bases n until the power of the digit sum is too small to match n^k.
18+
# The power of the digit sum is digitSum(n^k)^k, which is at most (9 * digitLength(n^k))^k.
19+
def compute():
20+
INDEX = 30 # 1-based
21+
limit = 1
22+
while True:
23+
candidates = set()
24+
k = 2
25+
while (1 << k) < limit:
26+
for n in itertools.count(2):
27+
pow = n**k
28+
if pow >= limit and len(str(pow)) * 9 < n:
29+
break
30+
if pow >= 10 and is_digit_sum_power(pow):
31+
candidates.add(pow)
32+
k += 1
33+
if len(candidates) >= INDEX:
34+
return str(sorted(candidates)[INDEX - 1])
35+
limit <<= 8
36+
37+
38+
def is_digit_sum_power(x):
39+
digitsum = sum(int(c) for c in str(x))
40+
if digitsum == 1: # Powers of 10 are never a power of 1
41+
return False
42+
pow = digitsum
43+
while pow < x:
44+
pow *= digitsum
45+
return pow == x
46+
47+
48+
if __name__ == "__main__":
49+
print(compute())

0 commit comments

Comments
 (0)