gpt4 book ai didi

java - 8-Puzzle Solution 无限执行

转载 作者:行者123 更新时间:2023-11-29 06:40:22 26 4
gpt4 key购买 nike

我正在寻找 8-puzzle 的解决方案使用 A* 算法 的问题。我找到了 this互联网上的项目。请查看文件 - proj1EightPuzzle。 proj1 包含程序的入口点(main() 函数),EightPuzzle 描述了拼图的特定状态。每个状态都是 8 拼图的一个对象。
感觉逻辑上没什么问题。但它会永远循环我尝试过的这两个输入:{8,2,7,5,1,6,3,0,4}{3,1,6, 8,4,5,7,2,0}。它们都是有效的输入状态。代码有什么问题?


注意

  • 为了更好地查看,请将代码复制到 Notepad++ 或其他文本中编辑器(具有识别java源文件的能力)因为代码中有很多注释。
  • 由于 A* 需要启发式,他们提供了使用的选项曼哈顿距离和计算数量的启发式错放的瓷砖。并确保执行最佳启发式首先,他们实现了一个PriorityQueuecompareTo()函数在 EightPuzzle 类中实现。
  • 可以通过更改 proj1 类的 main() 函数中的 p1d 的值来更改程序的输入。<
  • 我告诉我上面的两个输入存在解决方案的原因是因为小程序 here解决他们。请确保您从小程序的选项中选择了 8-puzzle。

    EDIT1
    我给了这个输入 {0,5,7,6,8,1,2,4,3}。花了大约 10 秒 并给出了 26 步 的结果。但是小程序在 0.0001 秒 内给出了 24 步 的结果,A*

    EDIT2
    在调试时,我注意到随着节点的扩展,新节点在一段时间后都具有启发式 - f_n1112。他们似乎永远不会减少。所以一段时间后,PriorityQueue(openset) 中的所有状态都具有 11 或 12 的启发式。因此没有太多选择,可以扩展到哪个节点。因为最小的是11,最大的是12,这正常吗?

    EDIT3
    这是发生无限循环的片段(在 proj1-astar())中。 openset是包含未展开节点的PriorityQueue,closedset是包含展开节点的LinkedList。

while(openset.size() > 0){

                    EightPuzzle x = openset.peek();


if(x.mapEquals(goal))
{

Stack<EightPuzzle> toDisplay = reconstruct(x);
System.out.println("Printing solution... ");
System.out.println(start.toString());
print(toDisplay);
return;

}
closedset.add(openset.poll());
LinkedList <EightPuzzle> neighbor = x.getChildren();
while(neighbor.size() > 0)
{
EightPuzzle y = neighbor.removeFirst();
if(closedset.contains(y)){
continue;
}
if(!closedset.contains(y)){
openset.add(y);
}
}

}




EDIT4

我找到了这个无限循环的原因。看我的回答。但是执行起来大概需要25-30秒,这是相当长的时间。 A* 应该比这快得多。小程序在 0.003 秒 内完成此操作。 我将奖励改进性能的赏金。


为了快速引用,我粘贴了两个没有注释的类:

八拼图


 import java.util.*;

public class EightPuzzle implements Comparable <Object> {


int[] puzzle = new int[9];
int h_n= 0;
int hueristic_type = 0;
int g_n = 0;
int f_n = 0;
EightPuzzle parent = null;


public EightPuzzle(int[] p, int h_type, int cost)
{
this.puzzle = p;
this.hueristic_type = h_type;
this.h_n = (h_type == 1) ? h1(p) : h2(p);
this.g_n = cost;
this.f_n = h_n + g_n;
}
public int getF_n()
{
return f_n;
}
public void setParent(EightPuzzle input)
{
this.parent = input;
}
public EightPuzzle getParent()
{
return this.parent;
}

public int inversions()
{
/*
* Definition: For any other configuration besides the goal,
* whenever a tile with a greater number on it precedes a
* tile with a smaller number, the two tiles are said to be inverted
*/
int inversion = 0;
for(int i = 0; i < this.puzzle.length; i++ )
{
for(int j = 0; j < i; j++)
{
if(this.puzzle[i] != 0 && this.puzzle[j] != 0)
{
if(this.puzzle[i] < this.puzzle[j])
inversion++;
}
}

}
return inversion;

}
public int h1(int[] list)
// h1 = the number of misplaced tiles
{
int gn = 0;
for(int i = 0; i < list.length; i++)
{
if(list[i] != i && list[i] != 0)
gn++;
}
return gn;
}
public LinkedList<EightPuzzle> getChildren()
{
LinkedList<EightPuzzle> children = new LinkedList<EightPuzzle>();
int loc = 0;
int temparray[] = new int[this.puzzle.length];
EightPuzzle rightP, upP, downP, leftP;
while(this.puzzle[loc] != 0)
{
loc++;
}
if(loc % 3 == 0){
temparray = this.puzzle.clone();
temparray[loc] = temparray[loc + 1];
temparray[loc + 1] = 0;
rightP = new EightPuzzle(temparray, this.hueristic_type, this.g_n + 1);
rightP.setParent(this);
children.add(rightP);

}else if(loc % 3 == 1){
//add one child swaps with right
temparray = this.puzzle.clone();
temparray[loc] = temparray[loc + 1];
temparray[loc + 1] = 0;

rightP = new EightPuzzle(temparray, this.hueristic_type, this.g_n + 1);
rightP.setParent(this);
children.add(rightP);
//add one child swaps with left
temparray = this.puzzle.clone();
temparray[loc] = temparray[loc - 1];
temparray[loc - 1] = 0;

leftP = new EightPuzzle(temparray, this.hueristic_type, this.g_n + 1);
leftP.setParent(this);
children.add(leftP);
}else if(loc % 3 == 2){
// add one child swaps with left
temparray = this.puzzle.clone();
temparray[loc] = temparray[loc - 1];
temparray[loc - 1] = 0;

leftP = new EightPuzzle(temparray, this.hueristic_type, this.g_n + 1);
leftP.setParent(this);
children.add(leftP);
}

if(loc / 3 == 0){
//add one child swaps with lower
temparray = this.puzzle.clone();
temparray[loc] = temparray[loc + 3];
temparray[loc + 3] = 0;

downP = new EightPuzzle(temparray, this.hueristic_type, this.g_n + 1);

downP.setParent(this);

children.add(downP);


}else if(loc / 3 == 1 ){
//add one child, swap with upper
temparray = this.puzzle.clone();
temparray[loc] = temparray[loc - 3];
temparray[loc - 3] = 0;

upP = new EightPuzzle(temparray, this.hueristic_type, this.g_n + 1);
upP.setParent(this);

children.add(upP);
//add one child, swap with lower
temparray = this.puzzle.clone();
temparray[loc] = temparray[loc + 3];
temparray[loc + 3] = 0;

downP = new EightPuzzle(temparray, this.hueristic_type, this.g_n + 1);
downP.setParent(this);

children.add(downP);
}else if (loc / 3 == 2 ){
//add one child, swap with upper
temparray = this.puzzle.clone();
temparray[loc] = temparray[loc - 3];
temparray[loc - 3] = 0;

upP = new EightPuzzle(temparray, this.hueristic_type, this.g_n + 1);
upP.setParent(this);

children.add(upP);
}

return children;
}
public int h2(int[] list)
// h2 = the sum of the distances of the tiles from their goal positions
// for each item find its goal position
// calculate how many positions it needs to move to get into that position
{
int gn = 0;
int row = 0;
int col = 0;
for(int i = 0; i < list.length; i++)
{
if(list[i] != 0)
{
row = list[i] / 3;
col = list[i] % 3;
row = Math.abs(row - (i / 3));
col = Math.abs(col - (i % 3));
gn += row;
gn += col;
}

}
return gn;
}

public String toString()
{
String x = "";
for(int i = 0; i < this.puzzle.length; i++){
x += puzzle[i] + " ";
if((i + 1) % 3 == 0)
x += "\n";
}
return x;
}
public int compareTo(Object input) {


if (this.f_n < ((EightPuzzle) input).getF_n())
return -1;
else if (this.f_n > ((EightPuzzle) input).getF_n())
return 1;
return 0;
}

public boolean equals(EightPuzzle test){
if(this.f_n != test.getF_n())
return false;
for(int i = 0 ; i < this.puzzle.length; i++)
{
if(this.puzzle[i] != test.puzzle[i])
return false;
}
return true;
}
public boolean mapEquals(EightPuzzle test){
for(int i = 0 ; i < this.puzzle.length; i++)
{
if(this.puzzle[i] != test.puzzle[i])
return false;
}
return true;
}

}

项目1

import java.util.*;

public class proj1 {

/**
* @param args
*/

public static void main(String[] args) {


int[] p1d = {1, 4, 2, 3, 0, 5, 6, 7, 8};
int hueristic = 2;
EightPuzzle start = new EightPuzzle(p1d, hueristic, 0);
int[] win = { 0, 1, 2,
3, 4, 5,
6, 7, 8};
EightPuzzle goal = new EightPuzzle(win, hueristic, 0);

astar(start, goal);



}

public static void astar(EightPuzzle start, EightPuzzle goal)
{
if(start.inversions() % 2 == 1)
{
System.out.println("Unsolvable");
return;
}
// function A*(start,goal)
// closedset := the empty set // The set of nodes already evaluated.
LinkedList<EightPuzzle> closedset = new LinkedList<EightPuzzle>();
// openset := set containing the initial node // The set of tentative nodes to be evaluated. priority queue
PriorityQueue<EightPuzzle> openset = new PriorityQueue<EightPuzzle>();

openset.add(start);


while(openset.size() > 0){
// x := the node in openset having the lowest f_score[] value
EightPuzzle x = openset.peek();

// if x = goal
if(x.mapEquals(goal))
{
// return reconstruct_path(came_from, came_from[goal])
Stack<EightPuzzle> toDisplay = reconstruct(x);
System.out.println("Printing solution... ");
System.out.println(start.toString());
print(toDisplay);
return;

}
// remove x from openset
// add x to closedset
closedset.add(openset.poll());
LinkedList <EightPuzzle> neighbor = x.getChildren();
// foreach y in neighbor_nodes(x)
while(neighbor.size() > 0)
{
EightPuzzle y = neighbor.removeFirst();
// if y in closedset
if(closedset.contains(y)){
// continue
continue;
}
// tentative_g_score := g_score[x] + dist_between(x,y)
//
// if y not in openset
if(!closedset.contains(y)){
// add y to openset
openset.add(y);
//
}
//
}
//
}
}

public static void print(Stack<EightPuzzle> x)
{
while(!x.isEmpty())
{
EightPuzzle temp = x.pop();
System.out.println(temp.toString());
}
}

public static Stack<EightPuzzle> reconstruct(EightPuzzle winner)
{
Stack<EightPuzzle> correctOutput = new Stack<EightPuzzle>();

while(winner.getParent() != null)
{
correctOutput.add(winner);
winner = winner.getParent();
}

return correctOutput;
}

}

最佳答案

这是一个建议。对于您的示例,我的计时器报告 0 毫秒。对于此处给出的更难的拼图,需要 31 步才能完成,需要 96 毫秒。

HashSet 对于闭集比您的链表更有意义。它具有 O(1) 时间插入和成员资格测试,其中您的链接列表需要的时间与列表的长度成正比,列表的长度在不断增长。

您正在使用额外的数据结构和代码,使您的程序比需要的更复杂和更慢。多思考,少编码,学习别人好的代码来克服这个问题。我的并不完美(没有任何代码是完美的),但它是一个起点。

我使用从每个图 block 的当前位置到其目标的曼哈顿距离的最大值作为启发式。启发式的选择不会影响解决方案中的步骤数,但极大地影响运行时间。例如,h=0 将产生强力广度优先搜索。

请注意,为了让 A* 提供最佳解决方案,启发式永远不会高估达到目标的实际最小步数。如果这样做,找到的解决方案可能不是最短的。我不肯定“反转”启发式具有此属性。

package eightpuzzle;

import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.PriorityQueue;

public class EightPuzzle {

// Tiles for successfully completed puzzle.
static final byte [] goalTiles = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };

// A* priority queue.
final PriorityQueue <State> queue = new PriorityQueue<State>(100, new Comparator<State>() {
@Override
public int compare(State a, State b) {
return a.priority() - b.priority();
}
});

// The closed state set.
final HashSet <State> closed = new HashSet <State>();

// State of the puzzle including its priority and chain to start state.
class State {
final byte [] tiles; // Tiles left to right, top to bottom.
final int spaceIndex; // Index of space (zero) in tiles
final int g; // Number of moves from start.
final int h; // Heuristic value (difference from goal)
final State prev; // Previous state in solution chain.

// A* priority function (often called F in books).
int priority() {
return g + h;
}

// Build a start state.
State(byte [] initial) {
tiles = initial;
spaceIndex = index(tiles, 0);
g = 0;
h = heuristic(tiles);
prev = null;
}

// Build a successor to prev by sliding tile from given index.
State(State prev, int slideFromIndex) {
tiles = Arrays.copyOf(prev.tiles, prev.tiles.length);
tiles[prev.spaceIndex] = tiles[slideFromIndex];
tiles[slideFromIndex] = 0;
spaceIndex = slideFromIndex;
g = prev.g + 1;
h = heuristic(tiles);
this.prev = prev;
}

// Return true iif this is the goal state.
boolean isGoal() {
return Arrays.equals(tiles, goalTiles);
}

// Successor states due to south, north, west, and east moves.
State moveS() { return spaceIndex > 2 ? new State(this, spaceIndex - 3) : null; }
State moveN() { return spaceIndex < 6 ? new State(this, spaceIndex + 3) : null; }
State moveE() { return spaceIndex % 3 > 0 ? new State(this, spaceIndex - 1) : null; }
State moveW() { return spaceIndex % 3 < 2 ? new State(this, spaceIndex + 1) : null; }

// Print this state.
void print() {
System.out.println("p = " + priority() + " = g+h = " + g + "+" + h);
for (int i = 0; i < 9; i += 3)
System.out.println(tiles[i] + " " + tiles[i+1] + " " + tiles[i+2]);
}

// Print the solution chain with start state first.
void printAll() {
if (prev != null) prev.printAll();
System.out.println();
print();
}

@Override
public boolean equals(Object obj) {
if (obj instanceof State) {
State other = (State)obj;
return Arrays.equals(tiles, other.tiles);
}
return false;
}

@Override
public int hashCode() {
return Arrays.hashCode(tiles);
}
}

// Add a valid (non-null and not closed) successor to the A* queue.
void addSuccessor(State successor) {
if (successor != null && !closed.contains(successor))
queue.add(successor);
}

// Run the solver.
void solve(byte [] initial) {

queue.clear();
closed.clear();

// Click the stopwatch.
long start = System.currentTimeMillis();

// Add initial state to queue.
queue.add(new State(initial));

while (!queue.isEmpty()) {

// Get the lowest priority state.
State state = queue.poll();

// If it's the goal, we're done.
if (state.isGoal()) {
long elapsed = System.currentTimeMillis() - start;
state.printAll();
System.out.println("elapsed (ms) = " + elapsed);
return;
}

// Make sure we don't revisit this state.
closed.add(state);

// Add successors to the queue.
addSuccessor(state.moveS());
addSuccessor(state.moveN());
addSuccessor(state.moveW());
addSuccessor(state.moveE());
}
}

// Return the index of val in given byte array or -1 if none found.
static int index(byte [] a, int val) {
for (int i = 0; i < a.length; i++)
if (a[i] == val) return i;
return -1;
}

// Return the Manhatten distance between tiles with indices a and b.
static int manhattanDistance(int a, int b) {
return Math.abs(a / 3 - b / 3) + Math.abs(a % 3 - b % 3);
}

// For our A* heuristic, we just use max of Manhatten distances of all tiles.
static int heuristic(byte [] tiles) {
int h = 0;
for (int i = 0; i < tiles.length; i++)
if (tiles[i] != 0)
h = Math.max(h, manhattanDistance(i, tiles[i]));
return h;
}

public static void main(String[] args) {

// This is a harder puzzle than the SO example
byte [] initial = { 8, 0, 6, 5, 4, 7, 2, 3, 1 };

// This is taken from the SO example.
//byte [] initial = { 1, 4, 2, 3, 0, 5, 6, 7, 8 };

new EightPuzzle().solve(initial);
}
}

关于java - 8-Puzzle Solution 无限执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13053455/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com