我已经编写了一个程序来解决使用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,你可能不需要通过其他课程。
答案 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)坐标都有一个唯一的条目;到目前为止发现成本最低的那个。