import java.math.BigInteger; import java.util.*; public class HammingTriple implements Comparable { // Precompute a couple of constants that we need all the time private static final BigInteger two = BigInteger.valueOf(2); private static final BigInteger three = BigInteger.valueOf(3); private static final BigInteger five = BigInteger.valueOf(5); private static final double logOf2 = Math.log(2); private static final double logOf3 = Math.log(3); private static final double logOf5 = Math.log(5); // The powers of this triple private int a, b, c; public HammingTriple(int a, int b, int c) { this.a = a; this.b = b; this.c = c; } public String toString() { return "[" + a + ", " + b + ", " + c + "]"; } public BigInteger getValue() { return two.pow(a).multiply(three.pow(b)).multiply(five.pow(c)); } public boolean equals(Object other) { if(other instanceof HammingTriple) { HammingTriple h = (HammingTriple) other; return this.a == h.a && this.b == h.b && this.c == h.c; } else { return false; } } // Return 0 if this == other, +1 if this > other, and -1 if this < other public int compareTo(HammingTriple other) { // equality if(this.a == other.a && this.b == other.b && this.c == other.c) { return 0; } // this dominates if(this.a >= other.a && this.b >= other.b && this.c >= other.c) { return +1; } // other dominates if(this.a <= other.a && this.b <= other.b && this.c <= other.c) { return -1; } // take the logarithms for comparison double log1 = this.a * logOf2 + this.b * logOf3 + this.c * logOf5; double log2 = other.a * logOf2 + other.b * logOf3 + other.c * logOf5; // are these different enough to be reliable? if(Math.abs(log1 - log2) > 0.0000001) { return (log1 < log2) ? -1: +1; } // oh well, looks like we have to do this the hard way return this.getValue().compareTo(other.getValue()); // (getting this far should be pretty rare, though) } public static BigInteger computeHamming(int n, boolean verbose) { if(verbose) { System.out.println("Hamming number #" + n); } long startTime = System.currentTimeMillis(); // The elements of the search frontier PriorityQueue frontierQ = new PriorityQueue(); int maxFrontierSize = 1; // Initialize the frontier frontierQ.offer(new HammingTriple(0, 0, 0)); // 1 while(true) { if(frontierQ.size() > maxFrontierSize) { maxFrontierSize = frontierQ.size(); } // Pop out the next Hamming number from the frontier HammingTriple curr = frontierQ.poll(); if(--n == 0) { if(verbose) { System.out.println("Time: " + (System.currentTimeMillis() - startTime) + " ms"); System.out.println("Frontier max size: " + maxFrontierSize); System.out.println("As powers: " + curr.toString()); System.out.println("As value: " + curr.getValue()); } return curr.getValue(); } // Current times five, if at origin in (a,b) plane if(curr.a == 0 && curr.b == 0) { frontierQ.offer(new HammingTriple(curr.a, curr.b, curr.c + 1)); } // Current times three, if at line a == 0 if(curr.a == 0) { frontierQ.offer(new HammingTriple(curr.a, curr.b + 1, curr.c)); } // Current times two, unconditionally curr.a++; frontierQ.offer(curr); // reuse the current HammingTriple object } } }