3

A* でn パズル問題を解くことができるプログラムを実装しました。状態のスペースが大きすぎるため、プリコンパイルできず、実行時に可能な状態を計算する必要があります。このように、A* は 3 パズルでは問題なく機能しますが、4 パズルでは時間がかかりすぎる可能性があります。線形競合で調整されたマンハッタン距離を使用すると、最適解が約 25 回の移動を必要とする場合でも高速であり、約 35 回で 10 秒、40 回で 180 秒かかります。私はまだそれ以上試していません。
これは、許容できる関数を使用しているため、すべての訪問済み状態を保持する必要があるためだと思いますが、一貫性がありません (ハミング距離とガシュニッヒ距離などでも試しました)。ソリューションの空間はグラフであるため、ヒューリスティックも一貫している必要があります。そうしないと、アルゴリズムがループしたり、最適でなくなったりする可能性があります。そのため、訪問したすべてのノードを保持しています (「AI: A modern approach」という本にも書かれています)。とにかく、このストレージはまったく遅くなりません。速度が低下するのは、訪問するノードのキューを順序付けたままにすることです。
そこで、私が見たように、このストレージを必要としない 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;
}
4

2 に答える 2

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

以下に提案する1回目の変更で所要時間がどの程度変わるかはわかりませんが、2回目の変更を行う必要があると確信しています。


最初の変更

コードを比較すると、この関数が

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

基本的にここでのあなたの機能です

public static State limitedSearch(State current, int limit)

JulienDramaixの実装には次のものがありません。

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

したがって、これらの2行を取り出してテストします。


2番目の変更

関数public static State solveIDAStar(State initialState)はwhileループで奇妙なことをします。

一度失敗した後、最大深度(制限)を無限に設定します。基本的に、最初の反復では、ヒューリスティックと同じくらい良い解決策を見つけようとします。次に、解決策を見つけようとします。これは反復深化ではありません。

反復深化とは、試行するたびに少し深くなることを意味します。

確かに、のwhileループを見ると、public PuzzleSolution resolve(State start, State goal)が見つかります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フォルダーにあります。

于 2012-08-20T07:26:00.583 に答える
0

小さな問題: あなたは、「何が遅くなるのかは、訪問するノードのキューを順番に保つことです.」と言いました. この場合、「優先ヒープ」を使用できます。これは、常に最小 (または最大) のアイテムをキューに返す部分的な順序付けされたキューであり、挿入、取得 - 削除は 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
}      

お役に立てれば...

于 2012-10-15T21:07:20.850 に答える