最佳中值函数

时间:2015-04-29 10:03:19

标签: java comparable

我正在试验一些代码,试图回答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),但我认为这不太重要。

我是否可以使用某种机制来正确生成矩阵?理想情况下也可用于生成minmax矩阵。

2 个答案:

答案 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,},},};

我仍然会对一种不那么奇怪的自我指涉的方法感兴趣。