### 使用迭代加深A星算法(IDA*)在Java中解决n-拼图(滑动拼图)问题

我已经实现了一个能够使用A*算法解决n-拼图问题的程序。由于状态空间太大,我无法预编译,必须在运行时计算可能的状态。这样,A*算法在3-拼图上运行良好,但在4-拼图上可能需要很长时间。使用调整后的曼哈顿距离和线性冲突,如果最优解大约需要25步,仍然很快,35步大约需要10秒,40步需要180秒。我还没有尝试更多步数。
我认为这是因为我必须保留所有访问过的状态,因为我使用的是可接受但(我认为)不一致的函数(我也尝试了汉明距离和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); // 这是一个全局变量        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;}

回答:

旧内容已移至下方。

更改以便newLimit可以跳过步骤(bestSolution相关内容):

State bestSolution; // 添加这个全局变量public static State solveIDAStar(State initialState) {    int limit = initialState.getManhattanDistance() + 2 * initialState.getLinearConflicts();    bestSolution = null; // 将全局变量重置为null    State result = null;    while(result == null) {        visitedStates.add(initialState); // 这是一个全局变量        newLimit = INFINITY;        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 &&                   (bestSolution == null || solution.getPathCost() < bestSolution.getPathCost()))                        bestSolution = solution; // 缓存迄今为止的最佳解            } else {                if(currentCost < newLimit)                    newLimit = currentCost;            }        }    }    return null;}

原始回答

我找到了一个开源实现。奇迹般地,它也是用Java写的。

可以在这里测试该应用程序:http://n-puzzle-solver.appspot.com/

特别相关的源代码是:http://code.google.com/p/julien-labs/source/browse/trunk/SlidingPuzzle/src/be/dramaix/ai/slidingpuzzle/server/search/IDAStar.java

不确定下面建议的第一个更改会对所需时间产生多大影响,但我相当确定你需要进行第二个更改。


第一个更改

通过比较代码,你会发现这个函数

private Node depthFirstSearch(Node current, int currentCostBound, State goal)

基本上就是你这里的函数

public static State limitedSearch(State current, int limit)

而Julien Dramaix的实现中没有:

if(!visitedStates.contains(s)) {    ...    visitedStates.add(s);

所以删除这两行来测试。


第二个更改

你的函数public static State solveIDAStar(State initialState)在while循环中做了一些奇怪的事情。

在你第一次失败后,你将最大深度(limit)设置为无穷大。基本上,第一次迭代,你尝试找到一个与你的启发式一样好的解。然后你尝试找到任何解。这不是迭代加深

迭代加深意味着每次尝试时,都要深入一点。

实际上,查看public PuzzleSolution resolve(State start, State goal)中的while循环,你会发现nextCostBound+=2;。这意味着,每次尝试时,尝试找到最多增加2步的解。


否则,其他一切看起来都相似(尽管你对State类的具体实现可能略有不同)。

如果效果更好,你可能还想尝试http://code.google.com/p/julien-labs/source/browse/#svn%2Ftrunk%2FSlidingPuzzle%2Fsrc%2Fbe%2Fdramaix%2Fai%2Fslidingpuzzle%2Fclient中的其他启发式方法。

启发式方法可以在server/search/heuristic文件夹中找到。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注