(Inspired by this question)
The digits of the cube, 41063625 (3453), can be permuted to produce two other cubes:
56623104 (3843) and 66430125 (4053). In fact, 41063625 is the smallest cube which has exactly three permutations of its digits which are also cube.
Find the smallest cube for which exactly five permutations of its digits are cube.
My attempt at #62 using Java 8 streams. I use a custom implementation of Spliterator.OfLong
(uncreatively named Generator
) that can be programmatically stopped when the Map
it is collect()
-ing to has the desired results. I also chose a custom Result
class to store related information about the generation. For example, the desired result for this challenge is:
---------- Size: 5 Map size: 7772 Last value: 8384 Ratio: 1.0787442099845599 Elapsed time (ms): 16 Result: 127035954683 Values: [5027, 7061, 7202, 8288, 8384] ----------
(This is after one "warm-up" round as shown in the main()
method)
- Is this a feasible approach, or does it really smell 'hack-ish'?
- Any other room for improvement?
My toKey()
method is based on my answer to this question.
public final class EulerSixtyTwo {
private static final ToLongFunction<Long> CUBE =
i -> (long) Math.pow(i.doubleValue(), 3);
private static Long toKey(Long input) {
int[] numbers = new int[10];
for (long i = CUBE.applyAsLong(input); i != 0; i /= 10) {
numbers[(int) (i % 10)]++;
}
int counter = 0;
long result = 0;
for (int i = 0; i < 10; counter += numbers[i++]) {
result += (int) ((Math.pow(10, numbers[i]) * i - 1) / 9)
* Math.pow(10, counter);
}
return Long.valueOf(result);
}
private static List<Long> toValue(Long input) {
return new ArrayList<>(Collections.singleton(input));
}
private static Result testFor(int limit) {
if (limit < 2) {
throw new IllegalArgumentException("Limit cannot be less than 2.");
}
Generator generator = new Generator();
long startTime = System.currentTimeMillis();
Map<Long, List<Long>> map = generator.longStream().boxed()
.collect(Collectors.toMap(EulerSixtyTwo::toKey, EulerSixtyTwo::toValue,
(a, b) -> {
generator.shouldStop(a.addAll(b) && a.size() == limit);
return a;
}));
long elapsedTime = System.currentTimeMillis() - startTime;
return new Result(toKey(generator.getLastValue()), map, elapsedTime);
}
private static final int START = 2;
private static final int END = 21;
public static void main(String[] args) {
// "warm-up"
IntStream.range(START, END).mapToObj(EulerSixtyTwo::testFor)
.forEach(v -> {});
IntStream.range(START, END).mapToObj(EulerSixtyTwo::testFor)
.forEach(System.out::println);
}
public static class Generator implements Spliterator.OfLong {
private boolean stop = false;
private long current = 1;
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public int characteristics() {
return Spliterator.ORDERED + Spliterator.DISTINCT;
}
@Override
public java.util.Spliterator.OfLong trySplit() {
return null;
}
@Override
public boolean tryAdvance(LongConsumer action) {
action.accept(current++);
return !stop;
}
public void shouldStop(boolean shouldStop) {
stop = shouldStop;
}
public Long getLastValue() {
return Long.valueOf(current - 1);
}
public LongStream longStream() {
return StreamSupport.longStream(this, false);
}
}
public static class Result {
private static final String WRAPPER = "\n----------\n";
private final long answer;
private final Map<Long, List<Long>> map;
private final List<Long> values;
private final long elapsedTime;
private final String toString;
public Result(Long key, Map<Long, List<Long>> map, long elapsedTime) {
this.map = Collections.unmodifiableMap(map);
this.values = map.get(key);
this.answer = CUBE.applyAsLong(values.get(0));
this.elapsedTime = elapsedTime;
this.toString = getStatistics();
}
public long getAnswer() {
return answer;
}
public List<Long> getValues() {
return values;
}
private String getStatistics() {
int mapSize = map.size();
long lastValue = values.get(values.size() - 1).longValue();
double ratio = lastValue / (double) mapSize;
return Stream.of("Size: " + values.size(), "Map size: " + mapSize,
"Last value: " + lastValue, "Ratio: " + ratio,
"Elapsed time (ms): " + elapsedTime,
"Result: " + getAnswer(),
"Values: " + getValues()).map(Object::toString)
.collect(Collectors.joining("\n", WRAPPER, WRAPPER));
}
@Override
public String toString() {
return toString;
}
}
}