我正在试验一些代码,试图回答Generic Method to find the median of 3 values,并试图确保compareTo
方法被调用的次数最少,并最大限度地提高了代码的效率,并且出现了有了这个:
// Flattens the results of a compareTo to -1, 0, 1
static int sign(int x) {
return x < 0 ? -1 : x == 0 ? 0 : 1;
}
// Use an enum to help.
enum ABC {
A() {
@Override
<T> T which(T a, T b, T c) {
return a;
}
},
B() {
@Override
<T> T which(T a, T b, T c) {
return b;
}
},
C() {
@Override
<T> T which(T a, T b, T c) {
return c;
}
},
INSANE() {
@Override
<T> T which(T a, T b, T c) {
// Something like a < b and b < c but c < a
return null;
}
};
abstract <T> T which(T a, T b, T c);
}
// Straight lookup.
private static final ABC[][][] median = {
{ // a < b
{ // b < c
// a < c
ABC.B,
// a == c
ABC.INSANE,
// a > c
ABC.INSANE
},
{ // b == c
// a < c
ABC.B,
// a == c
ABC.INSANE,
// a > c
ABC.INSANE
},
{ // b > c
// a < c
ABC.C,
// a == c
ABC.A,
// a > c
ABC.A
}
},
{ // a == b
{ // b < c
// a < c
ABC.B,
// a == c
ABC.INSANE,
// a > c
ABC.INSANE
},
{ // b == c
// a < c
ABC.INSANE,
// a == c
ABC.B,
// a > c
ABC.INSANE
},
{ // b > c
// a < c
ABC.A,
// a == c
ABC.B,
// a > c
ABC.A
}
},
{ // a > b
{ // b < c
// a < c
ABC.A,
// a == c
ABC.A,
// a > c
ABC.C
},
{ // b == c
// a < c
ABC.INSANE,
// a == c
ABC.INSANE,
// a > c
ABC.B
},
{ // b > c
// a < c
ABC.INSANE,
// a == c
ABC.INSANE,
// a > c
ABC.B
}
}
};
private static <T extends Comparable> T median(T a, T b, T c) {
System.out.print(" ab=" + sign(a.compareTo(b)) + " bc=" + sign(b.compareTo(c)) + " ac=" + sign(a.compareTo(c)));
// Least number of comparisons.
return median[sign(a.compareTo(b)) + 1][sign(b.compareTo(c)) + 1][sign(a.compareTo(c)) + 1]
.which(a, b, c);
}
// My test case.
private static <T extends Comparable> T median2(T a, T b, T c) {
List<T> medianHelper = Arrays.asList(a, b, c);
Collections.sort(medianHelper);
return medianHelper.get(1);
}
public void test() {
for (int a = -5; a <= 5; a += 5) {
for (int b = -3; b <= 3; b += 3) {
for (int c = -1; c <= 1; c++) {
System.out.print("(" + a + "," + b + "," + c + ")");
Integer m = median(a, b, c);
Integer m2 = median2(a, b, c);
System.out.print(" -> " + m + " & " + m2);
if (m != m2) {
System.out.print(" <-- WRONG");
}
System.out.println();
}
}
}
}
为了让它正常工作,我只使用sort them and pick the middle one
技术并手动调整矩阵,直到它返回相同的结果。
虽然这似乎有效,但它显然没有涵盖compareTo
不稳定的所有可能性(例如a < b && b < c && c < a
),但我认为这不太重要。
我是否可以使用某种机制来正确生成矩阵?理想情况下也可用于生成min
和max
矩阵。
答案 0 :(得分:1)
如果您只想拨打compareTo
3次(我不认为您可以通过2次通话使其工作),为什么不能使用简单的嵌套if / else:
public static <T extends Comparable> T median(T a, T b, T c) {
if (a.compareTo(b) <= 0) {
if (a.compareTo(c) <= 0) {
return b.compareTo(c) <= 0 ? b : c; //a <= b && a <= c, return min(b, c)
} else {
return a; //c < a <= b, return a
}
} else { // b < a
if (a.compareTo(c) <= 0) {
return a; // b < a <= c, return a
} else {
return b.compareTo(c) <= 0 ? c : b; //b < a && a > c, return max(b, c)
}
}
}
答案 1 :(得分:0)
我最终开始使用填充了INSANE
的矩阵并修复了与sort and pick one
方法不匹配的所有位置。
这还需要一个print
方法,将矩阵打印为Java源。
// Flattens the results of a compareTo to -1, 0, 1
static int sign(int x) {
return Integer.signum(x);
}
// Use an enum to help.
enum ABC {
A() {
@Override
<T> T which(T a, T b, T c) {
return a;
}
},
B() {
@Override
<T> T which(T a, T b, T c) {
return b;
}
},
C() {
@Override
<T> T which(T a, T b, T c) {
return c;
}
},
INSANE() {
@Override
<T> T which(T a, T b, T c) {
// Something like a < b and b < c but c < a
return null;
}
};
abstract <T> T which(T a, T b, T c);
}
private static <T extends Comparable> T byLookup(ABC[][][] matrix, T a, T b, T c) {
// Least number of comparisons.
return matrix[sign(a.compareTo(b)) + 1][sign(b.compareTo(c)) + 1][sign(a.compareTo(c)) + 1]
.which(a, b, c);
}
private static <T extends Comparable> T bySort(T a, T b, T c, int which) {
// Sort and pick one.
List<T> medianHelper = Arrays.asList(a, b, c);
Collections.sort(medianHelper);
return medianHelper.get(which);
}
private void checkMatrix(ABC[][][] matrix, int a, int b, int c, int which) {
// Use the two functions.
Integer m1 = byLookup(matrix, a, b, c);
Integer m2 = bySort(a, b, c, which);
if (!Objects.equal(m1, m2)) {
// Wrong! Fix it.
matrix[sign(Integer.compare(a, b)) + 1][sign(Integer.compare(b, c)) + 1][sign(Integer.compare(a, c)) + 1]
= m2 == a ? ABC.A : m2 == b ? ABC.B : ABC.C;
}
}
public void buildMatrix(String name, int which) {
// All INSANE to start with.
ABC[][][] matrix = new ABC[3][3][3];
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
for (int k = 0; k < matrix[0][0].length; k++) {
matrix[i][j][k] = ABC.INSANE;
}
}
}
// Test it.
for (int a = -5; a <= 5; a += 5) {
for (int b = -3; b <= 3; b += 3) {
for (int c = -1; c <= 1; c++) {
checkMatrix(matrix, a, b, c, which);
}
}
}
// Print it.
printMatrixAsJava(name, matrix);
}
public void buildMatrices() {
buildMatrix("min", 0);
buildMatrix("median", 1);
buildMatrix("max", 2);
}
static final String CR = "\r\n";
private void printMatrixAsJava(String name, ABC[][][] m) {
StringBuilder s = new StringBuilder();
String[] op = {"<", "=", ">"};
s.append("private static final ABC[][][] ").append(name).append(" = {" + CR);
for (int a = 0; a < 3; a++) {
s.append("\t{ // a ").append(op[a]).append(" b" + CR);
for (int b = 0; b < 3; b++) {
//{ // b < c
s.append("\t\t{ // b ").append(op[b]).append(" c" + CR);
for (int c = 0; c < 3; c++) {
// // a < c
//ABC.INSANE,
s.append("\t\t\t// a ").append(op[c]).append(" c" + CR);
s.append("\t\t\tABC.").append(m[a][b][c].name()).append("," + CR);
}
s.append("\t\t}," + CR);
}
s.append("\t}," + CR);
}
s.append("};" + CR);
System.out.println(s);
}
这导致:
// The lookups generated by the above.
private static final ABC[][][] min = {
{ // a < b
{ // b < c
// a < c
ABC.A,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b = c
// a < c
ABC.A,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b > c
// a < c
ABC.A,
// a = c
ABC.A,
// a > c
ABC.C,},},
{ // a = b
{ // b < c
// a < c
ABC.A,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b = c
// a < c
ABC.INSANE,
// a = c
ABC.A,
// a > c
ABC.INSANE,},
{ // b > c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.C,},},
{ // a > b
{ // b < c
// a < c
ABC.B,
// a = c
ABC.B,
// a > c
ABC.B,},
{ // b = c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.B,},
{ // b > c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.C,},},};
private static final ABC[][][] median = {
{ // a < b
{ // b < c
// a < c
ABC.B,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b = c
// a < c
ABC.B,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b > c
// a < c
ABC.C,
// a = c
ABC.A,
// a > c
ABC.A,},},
{ // a = b
{ // b < c
// a < c
ABC.A,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b = c
// a < c
ABC.INSANE,
// a = c
ABC.A,
// a > c
ABC.INSANE,},
{ // b > c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.A,},},
{ // a > b
{ // b < c
// a < c
ABC.A,
// a = c
ABC.A,
// a > c
ABC.C,},
{ // b = c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.B,},
{ // b > c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.B,},},};
private static final ABC[][][] max = {
{ // a < b
{ // b < c
// a < c
ABC.C,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b = c
// a < c
ABC.B,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b > c
// a < c
ABC.B,
// a = c
ABC.B,
// a > c
ABC.B,},},
{ // a = b
{ // b < c
// a < c
ABC.C,
// a = c
ABC.INSANE,
// a > c
ABC.INSANE,},
{ // b = c
// a < c
ABC.INSANE,
// a = c
ABC.A,
// a > c
ABC.INSANE,},
{ // b > c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.A,},},
{ // a > b
{ // b < c
// a < c
ABC.C,
// a = c
ABC.A,
// a > c
ABC.A,},
{ // b = c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.A,},
{ // b > c
// a < c
ABC.INSANE,
// a = c
ABC.INSANE,
// a > c
ABC.A,},},};
我仍然会对一种不那么奇怪的自我指涉的方法感兴趣。