我已经实现了一个能够用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;
}
答案 0 :(得分:3)
旧东西搬了下来。
更改以便newLimit可以跳过步骤(bestSolution内容):
State bestSolution; // add this global
public static State solveIDAStar(State initialState) {
int limit = initialState.getManhattanDistance() + 2 * initialState.getLinearConflicts();
bestSolution = null; // reset global to null
State result = null;
while(result == null) {
visitedStates.add(initialState); // It's a global variable
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; // cache solution so far
} else {
if(currentCost < newLimit)
newLimit = currentCost;
}
}
}
return null;
}
所以我找到了一个开源实现。奇迹般地,它也在java。
可以在此处测试应用程序: http://n-puzzle-solver.appspot.com/
不确定下面建议的第一个更改可能会改变所花费的时间,但我确信您需要进行第二次更改。
通过比较代码,你会发现这个函数
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循环中做了一些奇怪的事。
失败一次后,将最大深度(限制)设置为无穷大。基本上,第一次迭代,您尝试找到与您的启发式一样好的解决方案。然后你试着找到任何解决方案。这不是迭代深化。
迭代加深意味着每次尝试时都会更深入。
确实,在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尝试其他一些启发式方法。
启发式可在服务器/搜索/启发式文件夹中找到。
答案 1 :(得分:0)
一个小问题:你说“什么慢的是保持要访问的节点队列有序。”在这种情况下,您可以使用“优先级堆”。这是一个部分有序队列,它始终返回e队列中的最小(或最大)项,插入,检索 - 删除是O(log n),因此这可以使您的初始A *算法快一点。这里给你一个简单的实现,但是用C#编写,你需要把它翻译成Java ......
public class PriorityHeap<T>
{
private int count;
private int defaultLength = 10;
private PriorityHeapNode[] array;
private bool isMin;
/// <summary>
///
/// </summary>
/// <param name="isMin">true si quiere que la ColaHeap devuelva el elemento de menor Priority, falso si quiere que devuelva el de mayor</param>
public PriorityHeap(bool isMin)
{
this.count = 0;
this.isMin = isMin;
this.array = new PriorityHeapNode[defaultLength];
}
public PriorityHeap(bool isMin, int iniLength)
{
this.count = 0;
this.isMin = isMin;
this.defaultLength = iniLength;
this.array = new PriorityHeapNode[defaultLength];
}
public class PriorityHeapNode
{
T valor;
int _priority;
public PriorityHeapNode(T valor, int _priority)
{
this.valor = valor;
this._priority = _priority;
}
public T Valor
{
get
{ return this.valor; }
}
public double Priority
{
get
{ return this._priority; }
}
}
public int Count
{ get { return this.count; } }
/// <summary>
/// Devuelve true si la cola devuelve el valor de menor Priority, falso si el de mayor
/// </summary>
public bool IsMin
{ get { return isMin; } }
/// <summary>
/// Devuelve y quita el Valor Minimo si la cola lo permite,si no, retorna null
/// </summary>
/// <returns></returns>
public PriorityHeapNode GetTopAndDelete()
{
PriorityHeapNode toRet;
if (count > 0)
{
if (count == 1)
{
toRet = array[0];
array[0] = null;
count--;
return toRet;
}
else
{
toRet = array[0];
array[0] = array[count - 1];
array[count - 1] = null;
HeapyfiToDown(0);
count--;
return toRet;
}
}
else return null;
}
/// <summary>
/// Devuelve el tope pero no lo borra
/// </summary>
/// <returns></returns>
public PriorityHeapNode GetTop()
{
return array[0];
}
public void Insert(PriorityHeapNode p)
{
if (array.Length == count)
Add(p);
else array[count] = p;
count++;
HeapyfiToUp(count - 1);
}
public void Clear()
{
count = 0;
}
#region Private Functions
private int GetFather(int i)
{
return ((i + 1) / 2) - 1;
}
private int GetRightSon(int i)
{ return 2 * i + 2; }
private int GetLeftSon(int i)
{ return 2 * i + 1; }
private void Add(PriorityHeapNode p)
{
if (array.Length == count)
{
PriorityHeapNode[] t = new PriorityHeapNode[array.Length * 2];
for (int i = 0; i < array.Length; i++)
{
t[i] = array[i];
}
t[count] = p;
array = t;
}
}
private void HeapyfiToUp(int i)
{
if (isMin)
{
int father = GetFather(i);
if (father > -1 && array[father].Priority > array[i].Priority)
{
PriorityHeapNode t = array[father];
array[father] = array[i];
array[i] = t;
HeapyfiToUp(father);
}
}
else
{
int father = GetFather(i);
if (father > -1 && array[father].Priority < array[i].Priority)
{
PriorityHeapNode t = array[father];
array[father] = array[i];
array[i] = t;
HeapyfiToUp(father);
}
}
}
private void HeapyfiToDown(int i)
{
if (isMin)
{
#region HeapyFi To down Min
int l = GetLeftSon(i);
int r = GetRightSon(i);
if (r < count)
{
PriorityHeapNode right = array[r];
PriorityHeapNode left = array[l];
int t;
if (right != null && left != null)
{
t = left.Priority < right.Priority ? l : r;
}
else if (right != null)
t = r;
else if (left != null)
t = l;
else return;
if (array[t].Priority < array[i].Priority)
{
PriorityHeapNode temp = array[t];
array[t] = array[i];
array[i] = temp;
HeapyfiToDown(t);
}
}
else if (l < count)
{
PriorityHeapNode left = array[l];
int t;
if (left != null)
t = l;
else return;
if (array[t].Priority < array[i].Priority)
{
PriorityHeapNode temp = array[t];
array[t] = array[i];
array[i] = temp;
HeapyfiToDown(t);
}
}
#endregion
}
else
{
#region HeapyFi To down NOT Min
int l = GetLeftSon(i);
int r = GetRightSon(i);
if (r < count)
{
PriorityHeapNode right = array[r];
PriorityHeapNode left = array[l];
int t;
if (right != null && left != null)
{
t = left.Priority > right.Priority ? l : r;
}
else if (right != null)
t = r;
else if (left != null)
t = l;
else return;
if (array[t].Priority > array[i].Priority)
{
PriorityHeapNode temp = array[t];
array[t] = array[i];
array[i] = temp;
HeapyfiToDown(t);
}
}
else if (l < count)
{
PriorityHeapNode left = array[l];
int t;
if (left != null)
t = l;
else return;
if (array[t].Priority > array[i].Priority)
{
PriorityHeapNode temp = array[t];
array[t] = array[i];
array[i] = temp;
HeapyfiToDown(t);
}
}
#endregion
}
}
#endregion
}
希望这会有所帮助......