Java:实现hashCode()以确保int数组的排列始终返回相同的哈希值

Java:实现hashCode()以确保int数组的排列始终返回相同的哈希值,java,arrays,algorithm,Java,Arrays,Algorithm,我试图使我的hashCode实现为int数组的所有排列返回相同的hashCode 要求: 如果数组A是B的置换,那么A.equalsB必须产生true 两个数组彼此排列,必须返回相同的哈希代码。 我已经编写了函数,为我提供了数组的所有排列、旋转和反射,但我真的不明白如何让它们都返回相同的代码,因为没有对象属性作为代码的基础 到目前为止,我尝试的是收集所有排列的array.hashCode,将它们相加成一个长的数,除以排列的数量,并将结果作为int返回 虽然使用中间值感觉不是一个好的解决方案非常模

我试图使我的hashCode实现为int数组的所有排列返回相同的hashCode

要求:

如果数组A是B的置换,那么A.equalsB必须产生true 两个数组彼此排列,必须返回相同的哈希代码。 我已经编写了函数,为我提供了数组的所有排列、旋转和反射,但我真的不明白如何让它们都返回相同的代码,因为没有对象属性作为代码的基础

到目前为止,我尝试的是收集所有排列的array.hashCode,将它们相加成一个长的数,除以排列的数量,并将结果作为int返回

虽然使用中间值感觉不是一个好的解决方案非常模糊,因此可能会导致碰撞,但无论如何它都不起作用。我发现不在有效排列中的对象返回相同的哈希代码

示例1:反射 这两者相等,因为arr2是arr1的反射

int[] arr1 = {0,2,4,1,3}     int[] arr2 = {4,2,0,3,1}   
[X 0 0 0 0]                  [0 0 0 0 X]
[0 0 X 0 0]                  [0 0 X 0 0]
[0 0 0 0 X]                  [X 0 0 0 0]
[0 X 0 0 0]                  [0 0 0 X 0]
[0 0 0 X 0]                  [0 X 0 0 0]
例2:旋转 这两个是彼此的置换,因为arr2是旋转的arr1

int[] arr1 = {0,2,4,1,3}     int[] arr2 = {4,1,3,0,2}   
[X 0 0 0 0]                  [0 0 0 0 X]
[0 0 X 0 0]                  [0 X 0 0 0]
[0 0 0 0 X]                  [0 0 0 X 0]
[0 X 0 0 0]                  [X 0 0 0 0]
[0 0 0 X 0]                  [0 0 X 0 0]
问:我如何实现一个hashCode函数来为每个数组对象返回相同的哈希值,而每个数组对象都是彼此的排列,对于上面所有的例子都返回相同的hashCode

更新: 我无法对数组进行排序和比较的原因是,将要比较的所有数组都将包含值0..n-1。原因是索引表示棋盘行,而值表示放置皇后的列。看看你是否感兴趣。
因此,我无法通过先排序来计算hashcode。还有其他想法吗?

创建一个封装数组的类

hashCode方法需要执行可交换的操作,以便不同的排列具有相同的hash代码。计算一个哈希代码,它是数组中元素的总和。如果订单发生变化,金额不会发生变化


您还应该重写equals。

在计算哈希或在equals中比较数组之前对数组进行排序。

您可以简单地对数组进行排序,然后使用arrays.hashCode计算哈希代码


您的收藏看起来像一个包或多套。有几个库具有这种数据结构的实现。例如。

最简单的方法是对数组中的所有值求和,然后使用位混合器来分散结果中的位。无论顺序如何,所有值的总和都是相同的,因此可以保证数组的任何排列都会产生相同的值

例如:

int hash = 0;
for (int i = 0; i < array.length; ++i)
{
    hash += array[i];
}

// See link below for reference
hash ^= (hash >>> 20) ^ (hash >>> 12);
return h ^ (hash >>> 7) ^ (hash >>> 4);
我从你那里得到了比特混音器代码。那一页充满了你可能想知道的好信息


你也可以考虑在数组长度中工作,如果你要比较的数组不同。您还可以将结果乘以数组中的最高值或最低值等任何有助于您区分的内容。

根据您的描述,听起来您正在对N皇后问题进行暴力解决,从而生成电路板上皇后的每个可能位置,消除反射/旋转,以便留下所有独特的电路板布局,然后搜索可接受的布局。正如在其他答案中提到的,不能仅依靠hashCode来消除重复,因为即使编写良好的hash函数也可能发生冲突

相反,我建议为给定的等价旋转/反射集定义一个规范布局。一种可能的方法是为布局定义排序顺序,对元素进行两两比较,直到找到不相等的位置。给定布局的规范表示将是具有最低顺序的布局

然后,当您生成布局时,您要做的第一件事就是获取该布局的规范表示,并且只有在您还没有看到规范版本的情况下才能继续。例如:

 public class Chessboard implements Comparable<Chessboard> {

    private int[] rows;

    public boolean equals(Object other) {
      return other != null && 
             other instanceof Chessboard && 
             Arrays.equals(rows, other.rows);
    }

    public int hashCode() {
       return Arrays.hashCode(rows);
    }

    public int compareTo(Chessboard other) {
       if (rows.length != other.rows.length) {
          return rows.length - other.rows.length;
       }
       for (int i = 0; i < rows.length; i++) {
          int c = rows[i] - other.rows[i];
          if (c != 0) return c;
       }
       return 0;
    }

    public List<Chessboard> getPermutations() {
       /* Your permutations code here. */
    }

    public Chessboard getCanonicalLayout() {
       List<Chessboard> permutations = getPermutations();
       Collections.sort(permutations);
       return permutations.get(0);
    }

    public static void main(String[] args) {
       Set<Chessboard> checked = new HashSet<Chessboard>();
       for (Chessboard b : getAllLayouts()) {
          Chessboard c = b.getCanonicalLayout();
          if (checked.contains(c)) {
             continue;
          }
          checked.add(c);
          if (isSolution(c)) {
             System.out.println("Found a solution: " + c);
          }
       }
    }
 }

这就是我目前所做的,但是哈希代码的总和对于int来说太大了,所以我必须将其相加为long,然后除以置换量,从而返回中值。这到目前为止还不起作用,因为不是置换的数组也可以返回相同的结果hash@krystah:您将无法找到任何保证哈希代码唯一的方法。数组的数量是无限的,只有2^32个可能的哈希码。+1,一个交换的,结合的运算符是最好的一般答案,xor是典型的。由于特定的数据集仅使用较低的值,您可能希望对每个1哈希冲突进行异或运算,但这没关系。只要相同元素的两个排列返回相同的哈希代码,那么如果两个不同元素的数组返回相同的哈希代码就可以了。这是一个原因
为什么还应该覆盖equals。此外,我认为如果哈希代码和溢出的整数效率较低,这并不重要,但您可以返回排序数组的哈希代码。我不能使用这种方法,因为所有有效和无效数组都将包含相同的值0..n-1。那么呢?hashCode的约定是两个相等的对象必须具有相同的hashCode。并不是说不相等的对象必须有不同的哈希代码。这是不可能的,因为可能存在无限多个对象,只有2^32个整数值。排序后的有效置换看起来与排序后的无效置换相同,因此无效置换将返回与有效置换相同的哈希代码。我没有得到什么?@krystah它被称为。啊,我现在得到了。很抱歉。你要对数组进行排序以计算其哈希代码吗?我希望这是一个小阵列@equals也需要JimMischel排序,因此无论如何都必须使用它。我将以规范的排序形式存储数组。这应该不是问题,因为所有这些对象都是相等的。排序只在数组更改时使用。相等性测试不需要排序。您可以从一个数组中创建哈希映射,并从另一个数组中查找每个项。这是启用的,而排序在日志n上。@JimMischel True,但由于我们讨论的是int数组,因此对于大多数实际大小的问题,在装箱哈希表时发生的对象分配将较慢。当然,除非我们运行自己的int哈希表实现。