【问题标题】:"Comparison method violates its general contract!", everything seems OK“比较方法违反了它的总合同!”,一切似乎都OK
【发布时间】:2015-08-07 14:13:17
【问题描述】:

我知道here 中描述的 Java(以及一般情况)中的比较规则。
我有一个字符串数组列表。
每个字符串代表忽略花色的德州扑克手牌。
每个字符串的长度正好是 13 个字符。
每个字符串仅由和为 7 的数字组成。

例如,“0100300200100”代表一手牌,由一张 3、三张 6、两张 9 和一张 Q 组成。
(在这种情况下,这手牌代表满屋 - 满是 9 的 6)。

我想根据扑克牌的强度对这个列表进行排序。
我有以下实现 Comparator 的比较方法的 java 代码。

final Comparator<String> COMBINATION_ORDER = new Comparator<String>() {
    @Override
    public int compare(String c1, String c2) {
        if (c1.indexOf('4') != -1 || c2.indexOf('4') != -1) {  // Four of a kind
            if (c1.indexOf('4') == c2.indexOf('4')) {
                for (int i = 12; i >= 0; i--) {
                    if (c1.charAt(i) != '0' && c1.charAt(i) != '4') {
                        if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
                            return 0;
                        }
                        return 1;
                    }
                    if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
                        return -1;
                    }
                }
            }
            return c1.indexOf('4') - c2.indexOf('4');
        }
        int tripleCount1 = StringFunctions.countOccurrencesOf(c1, "3");
        int tripleCount2 = StringFunctions.countOccurrencesOf(c2, "3");
        if (tripleCount1 > 1 || (tripleCount1 == 1 && c1.indexOf('2') != -1) || tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) {  // Full house
            int higherTriple = c1.lastIndexOf('3');
            if (higherTriple == c2.lastIndexOf('3')) {
                for (int i = 12; i >= 0; i--) {
                    if (i == higherTriple) {
                        continue;
                    }
                    if (c1.charAt(i) == '2' || c1.charAt(i) == '3') {
                        if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
                            return 0;
                        }
                        return 1;
                    }
                    if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
                        return -1;
                    }
                }
            }
            return higherTriple - c2.lastIndexOf('3');
        }
        return 0;
    }
};

同时,我仅指四种类型和满屋。 这意味着每一手牌都将被视为彼此平等(但不如四牌或满屋)。

但是当我在排序时:

combinations.sort(COMBINATION_ORDER);

(其中 combinations 是我的 ArrayList)。

我得到一个异常。

Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.TimSort.mergeLo(TimSort.java:773)
    at java.util.TimSort.mergeAt(TimSort.java:510)
    at java.util.TimSort.mergeCollapse(TimSort.java:437)
    at java.util.TimSort.sort(TimSort.java:241)
    at java.util.Arrays.sort(Arrays.java:1512)
    at java.util.ArrayList.sort(ArrayList.java:1454)
    at Poker.main(Poker.java:120)

请帮助我了解代码有什么问题。
非常感谢。

编辑:

正如@ajb 所说,我没有考虑没有满屋的三类。

解决方案:

final Comparator<String> COMBINATION_ORDER = new Comparator<String>() {
    @Override
    public int compare(String c1, String c2) {
        if (c1.indexOf('4') != -1 || c2.indexOf('4') != -1) {  // Four of a kind
            if (c1.indexOf('4') == c2.indexOf('4')) {
                for (int i = 12; i >= 0; i--) {
                    if (c1.charAt(i) != '0' && c1.charAt(i) != '4') {
                        if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
                            return 0;
                        }
                        return 1;
                    }
                    if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
                        return -1;
                    }
                }
            }
            return c1.indexOf('4') - c2.indexOf('4');
        }
        int tripleCount1 = StringFunctions.countOccurrencesOf(c1, "3");
        int tripleCount2 = StringFunctions.countOccurrencesOf(c2, "3");
        if (tripleCount1 > 1 || (tripleCount1 == 1 && c1.indexOf('2') != -1)) {      // c1 Full house
            if (tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) {  // c2 Full house too
                int higherTriple = c1.lastIndexOf('3');
                if (higherTriple == c2.lastIndexOf('3')) {
                    for (int i = 12; i >= 0; i--) {
                        if (i == higherTriple) {
                            continue;
                        }
                        if (c1.charAt(i) == '2' || c1.charAt(i) == '3') {
                            if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
                                return 0;
                            }
                            return 1;                                                // only c1 Full house
                        }
                        if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {            // only c2 Full house
                            return -1;
                        }
                    }
                }
                return higherTriple - c2.lastIndexOf('3');
            }
            return 1;
        }
        if (tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) {
            return -1;
        }
        return 0;
    }
};

【问题讨论】:

  • this question 的可能重复项?
  • 那么你将如何检测冲洗? :)
  • 不仔细看,我认为你的逻辑是错误的。如果(c1.indexOf('4') == c2.indexOf('4')) 为真,那么你有一副牌,里面有八张相同的牌。这并不能解释异常,但它让我认为您的算法可能存在其他错误。
  • @ajb,为了检测 Flush 我使用了另一个代码。当我确定没有 Flush 时使用此代码。

标签: java collections compare comparator comparable


【解决方案1】:

您的比较器必须遵守的条件之一是它必须是可传递的。即如果 A > B 且 B > C,则 A > C。如果比较器不遵循此规则,则排序可能会遇到排序与预期不符的情况,然后会抛出异常.

您的算法中至少存在一个逻辑错误。 (可能还有其他错误,但我绝对可以发现这个,它肯定会导致异常。)问题是当一只手有满堂彩,而另一手有 3 个同类但不是满堂彩。你的代码并不总是让整个房子变得更大。如果 3-of-a-kind 是 3of-of-a-high 牌高于满堂的三张牌,则 3-of-a-kind 会比较大。所以说一只手是KKK8743,一只是QQQ6632,一只是JJJ8743。您的代码错误地使 KKK8743 > QQQ6632。它还说 QQQ6632 > JJJ8743。但是它也说 KKK8743 = JJJ8743,所以违反了传递性。

【讨论】:

  • 我犯了一个严重的错误。你是对的,我没有注意到这一点。我纠正了它。谢谢。
猜你喜欢
  • 1970-01-01
  • 2016-11-18
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多