【发布时间】:2012-08-15 12:33:13
【问题描述】:
我已经实现了一个能够用 A* 解决 n-puzzle problem 的程序。由于状态空间太大,我无法预编译它,我必须在运行时计算可能的状态。以这种方式,A* 对于 3 拼图工作正常,但对于 4 拼图可能需要太长时间。使用线性冲突调整的曼哈顿距离,如果最优解需要大约 25 次移动仍然很快,大约 35 需要 10 秒,对于 40 需要 180 秒。我还没有尝试更多。
我认为那是因为我必须保留所有访问过的状态,因为我使用的函数是可接受的,但(我认为)不一致(我也尝试了 Hamming 和 Gaschnig 距离等等)。由于解决方案的空间是一个图,因此启发式也必须是一致的,否则算法可能会循环或不是最优的。这就是为什么我保留所有访问过的节点(它也写在“AI:一种现代方法”一书中)。但无论如何,这个存储一点也不慢。缓慢的是保持要访问的节点队列有序。
所以我决定尝试 IDA*,正如我所见,它不需要这种存储(但我仍然必须保留所有访问过的状态以避免循环)。对于需要 35 次或更少移动的解决方案,它会更快,但对于需要 40 次移动的解决方案则要慢得多。
这是我的代码。我是不是做错了什么?
public static State solveIDAStar(State initialState) {
int limit = initialState.getManhattanDistance() + 2 * initialState.getLinearConflicts();
State result = null;
while(result == null) {
visitedStates.add(initialState); // It's a global variable
result = limitedSearch(initialState, limit);
limit = newLimit;
visitedStates.clear();
}
return result;
}
public static State limitedSearch(State current, int limit) {
for(State s : current.findNext()) {
if(s.equals(GOAL)) {
s.setParent(current);
return s;
}
if(!visitedStates.contains(s)) {
s.setPathCost(current.getPathCost() + 1);
s.setParent(current);
int currentCost = s.getManhattanDistance() + 2 * s.getLinearConflicts() + s.getPathCost();
if(currentCost <= limit) {
visitedStates.add(s);
State solution = limitedSearch(s, limit);
if(solution != null)
return solution;
} else {
if(currentCost < newLimit)
newLimit = currentCost;
}
}
}
return null;
}
【问题讨论】:
-
你在哪里修改
current?正如我所看到的 - 只有当源与目标之间存在直接边缘时,算法才会返回答案 - 但我可能在这里遗漏了一些东西。你能澄清一下吗? -
@amit 我不修改对象本身,但我递归地调用该函数,每次给出当前的继任者。这是一个正常的递归 DFS,具有深度限制(使用启发式方法选择)。
-
是的,我因为某种原因错过了递归调用。感谢您的澄清。
标签: java artificial-intelligence puzzle graph-algorithm heuristics