使用A *算法和曼哈顿启发式解决8个难题

时间:2013-03-13 10:26:29

标签: java algorithm

我已经编写了一个程序来解决使用A *算法和曼哈顿启发式算法的8个难题,但是对于所有输入,甚至对于正确的输出,程序似乎都不能正常工作(最小移动次数),扩大的国家数量远远大于通常的数量。

我的课程有四个班级:
游戏状态:代表游戏
AStar:AStar算法
AStarList:用于表示打开和关闭列表的数据结构。 (我知道我的数据结构在性能方面非常糟糕。我稍后会对其进行改进)
应用

以下是代码的一部分:

(很抱歉代码很大。我怀疑我的AStar算法出了问题。所以,你可能不需要通过其他类。)

ASTAR

public class AStar {

    public static void solve(GameState gameStateToSolve) {
        AStarList openList = new AStarList();
        AStarList closedlList = new AStarList();
        openList.add(gameStateToSolve);
        int iteration = 1;
        while (!openList.isEmpty()) {
            System.out.println((iteration++));
            GameState current = openList.getNext();
            if (current.isGoalState()) {
                current.print();
                return;
            }
            GameState children[] = current.expand();
            closedlList.addWithoutDuplication(current);
            for (int i = 0; i < children.length; i++) {
                boolean presentInOpenList = openList.isPresent(children[i]);
                boolean presentInClosedList = closedlList.isPresent(children[i]);
                if (!presentInOpenList && !presentInClosedList) {
                    openList.add(children[i]);
                } else if (presentInClosedList && !presentInOpenList) {
                    if (closedlList.getCostOf(children[i]) > children[i].getMovementsCount()) {
                        closedlList.remove(children[i]);
                        openList.add(children[i]);
                    }
                } else if (presentInOpenList && !presentInClosedList) {
                    if (openList.getCostOf(children[i]) > children[i].getMovementsCount()) {
                        openList.remove(children[i]);
                        openList.add(children[i]);
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        solve(new GameState(
                new int[]{0,7,3,1,8,6,5,4,2},
                new ArrayList<Integer>(),
                GameState.NUMBERS_ARRAY));
    }
}

AStarList

public class AStarList {

    ArrayList<GameState> list;

    public AStarList() {
        list = new ArrayList<>();
    }

    public boolean isPresent(GameState gameState) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals(gameState)) {
                return true;
            }
        }
        return false;
    }

    public void remove(GameState gameState) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals(gameState)) {
                list.remove(i);
            }
        }
    }

    public void add(GameState gameState) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).manhattanDistance() > gameState.manhattanDistance()) {
                list.add(i, gameState);
                return;
            }
        }
        list.add(gameState);
    }

    public void addWithoutDuplication(GameState gameState) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals(gameState)) {
                list.remove(i);
                list.add(i, gameState);
            }
            if (list.get(i).manhattanDistance() > gameState.manhattanDistance()) {
                list.add(i, gameState);
                return;
            }
        }
        list.add(gameState);
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }

    public GameState getNext() {
        return list.remove(0);
    }

    public int getHeuristicOf(GameState gameState) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals(gameState)) {
                return list.get(i).manhattanDistance();
            }
        }
        throw new RuntimeException();
    }

    public int getCostOf(GameState gameState) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals(gameState)) {
                return list.get(i).getMovementsCount();
            }
        }
        return -1;
    }
}

游戏状态

public final class GameState1 {



    public GameState1(GameState gameState) {
       // creates a GameState exactly similar to the one passed
    }

    public GameState1(int[] array, ArrayList<Integer> movements, int type) {
     //...
    }

    public int getMovementsCount() {
     // returns number of movements made so far
    }

    public int[] getPositionsArrayOf(int[] numbersArray) {
        //...
    }

    public int[] getNumbersArrayOf(int[] positionsArray) {
        //...
    }

    public void move(int direction) {
        //...
    }

    public GameState getStateOnMovement(int direction) {
       //...
    }


    public boolean movePossible(int direction) {
        //...
    }

    public int[] getPossibleMovements() {
       //...
    }

    public GameState[] expand() {
       //..
    }

    public boolean equals(GameState anotherState) {
     // returns true if the board state is the same
    }

    public boolean isGoalState() {
      // returns true if it is goal state
    }

    public void print() {
        //...
    }




    public int numberOfInversions() {
        // returns number of inversions
    }

    public boolean isSolvable() {
       //returns true if solvable
    }

    public int manhattanDistance() {
     // returns manhattan distance
    }

   }

很抱歉代码很大。我怀疑我的AStar算法有问题。 S0,你可能不需要通过其他课程。

1 个答案:

答案 0 :(得分:2)

我还没有详尽地阅读代码,但我认为这可能是因为你只是通过启发式排序开放列表,而不是通过总成本函数。我确定你知道......

f(x) = g(x) + h(x)

到目前为止,g(x)是路径费用,h(x)是曼哈顿距离。

在方法AStarList.add()中尝试更改行

if (list.get(i).manhattanDistance() > gameState.manhattanDistance()) {

if (list.get(i).getCost() > gameState.getCost()) {

GameState.cost()

的位置
public int getCost() {
    return getMovementsCount() + manhattanDistance();
}

编辑:我也注意到你处理相邻节点看起来有点奇怪。您永远不应该从关闭列表中删除任何内容。首先,如果关闭列表已包含与该节点相同或更短的路径,则您希望丢弃邻居(即children[i])。第二,如果邻居是新的(即不在打开列表中)或者我们找到了打开列表中同一节点的较短路径,则将其添加到打开列表中。

boolean presentInOpenList = openList.isPresent(children[i]);
boolean presentInClosedList = closedlList.isPresent(children[i]);

if (presentInClosedList && children[i].getMovementsCount() >= closedlList.getCostOf(children[i])) {
    // Ignore this node
    continue;
}

if (!presentInOpenList || openList.getCostOf(children[i]) > children[i].getMovementsCount()) {
    openList.add(children[i]);
}

对于您的打开/关闭列表,使用Map而不是List可能会很好,因为您要确保每个(x,y)坐标都有一个唯一的条目;到目前为止发现成本最低的那个。