-
Notifications
You must be signed in to change notification settings - Fork 8
/
HammingTriple.java
114 lines (97 loc) · 4.02 KB
/
HammingTriple.java
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
import java.math.BigInteger;
import java.util.PriorityQueue;
public class HammingTriple implements Comparable<HammingTriple> {
// 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;
private final int b;
private final int 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<HammingTriple> 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);
assert curr != null;
System.out.println("As powers: " + curr);
System.out.println("As value: " + curr.getValue());
}
assert curr != null;
return curr.getValue();
}
// Current times five, if at origin in (a,b) plane
assert curr != null;
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
}
}
}