我写了一个名为PuzzleBoard的类,代表一个nxn板。我将在HashSet中保留几个PuzzleBoard对象,因此我必须覆盖'int hashCode()'方法。
以下是我班级的字段:
private int N;
private int[][] puzzle;
private int blankCellX;
private int blankCellY;
private int cost;
Eclipse为我自动生成的是:
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + N;
result = prime * result + blankCellX;
result = prime * result + blankCellY;
result = prime * result + cost;
result = prime * result + Arrays.hashCode(puzzle);
return result;
}
认为这种方法没有考虑二维数组的内容,我将其改为:
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + N;
result = prime * result + blankCellX;
result = prime * result + blankCellY;
result = prime * result + cost;
for (int i = 0; i < N; ++i)
result = prime * result + Arrays.hashCode(puzzle[i]);
return result;
}
然而,这种方法的问题是完成时间太长:O(N ^ 2) 此外; 'result'变量很可能会溢出。
现在,我的问题是,如何编写一个不需要太长时间才能完成的高效哈希方法。此外;在HashSet中插入或搜索对象应该是有效的(接近恒定时间)。
在最坏的情况下,N将为10,HashSet将包含~1000 PuzzleBoards。
为什么我要做这一切? 我正在使用A *算法实现N-Puzzle问题的解决方案。因此,在算法的某个阶段,给定当前节点(板的配置),我正在向上,向下,向右或向左移动空白单元以生成新的子节点。因此,拼图配置通常相差1或2个单元格。我将所有已探索的节点存储在HashSet中。
提前致谢=)
答案 0 :(得分:1)
哈希码不需要 是唯一的,如果它们更好的话。由于HashSet(~1000)中的项目数量相对较少,因此您可以选择少量合适的数据进行哈希处理。例如,您可能只需要“拼图”表的第一行,或者“成本”变量可能因不同的实例而大不相同,您可以将其用作差异的良好来源。
结果是否溢出并不重要:如果可能,您想要的是让不同的对象返回不同的哈希码。哈希的实际值并不重要。
答案 1 :(得分:1)
此方法未考虑二维数组的内容
您也可以使用util.Arrays#deepHashCode()
。
然而,这种方法的问题是完成时间太长:O(N ^ 2)
如果你想要散列其中的所有N ^ 2个整数,你不能走得更快?如果N最多为10,那么Big-O符号是什么? O(n^2)
并不意味着缓慢。我不认为你的hashCode方法效率低下。低效率或某些O(n^2)
很可能在其他地方......如果经常调用此方法(并且PuzzleBoard是不可变的),您可能希望缓存hashCode值。
'result'变量很可能会溢出。
没问题!溢出在Java中定义。
此外;在HashSet中插入或搜索对象应该是有效的(接近恒定时间)。
插入很可能只是摊销常量时间。当HashSet变满时,将创建一个新的更大的HashSet。所有元素都复制在其中,所有的hashCodes都必须再次计算。尝试为HashSet设置initialCapacity?
result = prime * result + cost;
您确定要将成本(我认为是深度)包含在equals和hashCode中吗?两个配置是相同的,无论我走多少步,对吧?
~1000 PuzzleBoards
如果我没记错的话,上次我解决了这个难题,我已经很多超过1000个配置。