迭代深化一个星(IDA *)来解决Java中的n-puzzle(滑动拼图)

时间:2012-08-20 06:42:43

标签: java artificial-intelligence puzzle graph-algorithm heuristics

我已经实现了一个能够用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;
}

2 个答案:

答案 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/

与源代码特别相关的是: 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循环中做了一些奇怪的事。

失败一次后,将最大深度(限制)设置为无穷大。基本上,第一次迭代,您尝试找到与您的启发式一样好的解决方案。然后你试着找到任何解决方案。这不是迭代深化

迭代加深意味着每次尝试时都会更深入。

确实,在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
}      

希望这会有所帮助......