forked from nayuki/Project-Euler-solutions
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathp060.java
More file actions
127 lines (104 loc) · 3.26 KB
/
p060.java
File metadata and controls
127 lines (104 loc) · 3.26 KB
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
/*
* Solution to Project Euler problem 60
* Copyright (c) Project Nayuki. All rights reserved.
*
* https://www.nayuki.io/page/project-euler-solutions
* https://github.com/nayuki/Project-Euler-solutions
*/
import java.util.Arrays;
import java.util.BitSet;
public final class p060 implements EulerSolution {
public static void main(String[] args) {
System.out.println(new p060().run());
}
private static final int PRIME_LIMIT = 100000; // Arbitrary initial cutoff
private int[] primes = Library.listPrimes(PRIME_LIMIT);
// Memoization
private BitSet isConcatPrimeKnown;
private BitSet isConcatPrime;
public String run() {
isConcatPrimeKnown = new BitSet(primes.length * primes.length);
isConcatPrime = new BitSet(primes.length * primes.length);
int sumLimit = PRIME_LIMIT;
while (true) {
int sum = findSetSum(new int[]{}, 5, sumLimit - 1);
if (sum == -1) // No smaller sum found
return Integer.toString(sumLimit);
sumLimit = sum;
}
}
/*
* Tries to find any suitable set and return its sum, or -1 if none is found.
* A set is suitable if it contains only primes, its size is 'targetSize',
* its sum is less than or equal to 'sumLimit', and each pair concatenates to a prime.
* 'prefix' is an array of ascending indices into the 'primes' array,
* which describes the set found so far.
* The function blindly assumes that each pair of primes in 'prefix' concatenates to a prime.
*
* For example, findSetSum(new int[]{1, 3, 28}, 5, 10000) means "find the sum of any set
* where the set has size 5, consists of primes with the lowest elements being {3, 7, 109},
* has sum 10000 or less, and has each pair concatenating to form a prime".
*/
private int findSetSum(int[] prefix, int targetSize, int sumLimit) {
if (prefix.length == targetSize) {
int sum = 0;
for (int i : prefix)
sum += primes[i];
return sum;
} else {
int i;
if (prefix.length == 0)
i = 0;
else
i = prefix[prefix.length - 1] + 1;
outer:
for (; i < primes.length && primes[i] <= sumLimit; i++) {
for (int j : prefix) {
if (!isConcatPrime(i, j) || !isConcatPrime(j, i))
continue outer;
}
int[] appended = Arrays.copyOf(prefix, prefix.length + 1);
appended[appended.length - 1] = i;
int sum = findSetSum(appended, targetSize, sumLimit - primes[i]);
if (sum != -1)
return sum;
}
return -1;
}
}
// Tests whether parseInt(toString(x) + toString(y)) is prime.
private boolean isConcatPrime(int x, int y) {
int index = x * primes.length + y;
if (isConcatPrimeKnown.get(index))
return isConcatPrime.get(index);
x = primes[x];
y = primes[y];
int mult = 1;
for (int temp = y; temp != 0; temp /= 10)
mult *= 10;
boolean result = isPrime((long)x * mult + y);
isConcatPrimeKnown.set(index);
isConcatPrime.set(index, result);
return result;
}
private boolean isPrime(long x) {
if (x < 0)
throw new IllegalArgumentException();
else if (x == 0 || x == 1)
return false;
else {
long end = Library.sqrt(x);
for (int p : primes) {
if (p > end)
break;
if (x % p == 0)
return false;
}
for (long i = primes[primes.length - 1] + 2; i <= end; i += 2) {
if (x % i == 0)
return false;
}
return true;
}
}
}