以前對A*算法的理解停留在人工智能書本上面,但是最近2天看了Stanford的一個遊戲介紹網站http://theory.stanford.edu/~amitp/GameProgramming/之後,覺得對A*算法有了比較深刻的瞭解。那個網站對A*描述沒有課本1條又1條的公式化,更多的是圖形,結合遊戲本身實際來描述,覺得幫助很大。
現在用Java編寫了一個算法演示,效果還不錯,不過現在open列表是鏈表形式,效率o(n)比較差,下一步可以考慮用堆插入查找來實現o(lgn)(不過其實現在100×100看起來還可以吧,暫時沒有什麼動力做,看以後需要吧)
附:算法源代碼,不妥之處,還望各位指正。
AStar.java(算法核心)
*
*/
package Arithmetic;
import java.util.*;
import GUI.*;
/** *//**
* @author 馮家耀
*
*/
public class AStar ...{
final int BARRIER = -3;
final int START = -1;
final int END = -2;
Node [][]grid;
List<Node> open = new LinkedList();
Set close = new HashSet();
Node startNode;
Node endNode;
int [][]result;//1:找過 2:找到 0:空
final int EVEN_FIND = 1;
final int SUC_FIND = 2;
final int NOT_FIND = 0;
int gridRows;
int gridCols;
int D = 1;
public AStar(GridValue [][]g)...{
if(g==null)return;
gridRows = g.length;
gridCols = g[0].length;
grid = new Node[gridRows][gridCols];
result = new int[gridRows][gridCols];
boolean hasfindfirst = false;
boolean hasfindend = false;
for(int i=0;i<gridRows;i++)...{
for(int j = 0; j < gridCols; j++)...{
grid[i][j] = new Node(i,j,g[i][j].toInt());
if(! hasfindfirst)...{
if(grid[i][j].cost == START)...{
hasfindfirst = true;
startNode = grid[i][j];
}
}
if(! hasfindend)...{
if(grid[i][j].cost == END)...{
hasfindend = true;
endNode = grid[i][j];
}
}
}
}
}
class Node...{
Node parent;
int row;
int col;
int cost;
int f;
int g;
public Node(int row,int col, int cost)...{
this.row = row;
this.col = col;
this.cost = cost;
}
@Override
public boolean equals(Object obj) ...{
if(obj instanceof Node && ((Node)obj).row == row && ((Node)obj).col == col)return true;
else return false;
}
@Override
public int hashCode() ...{
return row*gridCols+col;
}
}
int estimateH(Node node)...{
//return D*( (Math.abs(node.row) - endNode.row) + Math.abs(node.col - endNode.col) );
//return D*( Math.max((Math.abs(node.row) - endNode.row) , Math.abs(node.col - endNode.col)) );
int deltay = node.row - endNode.row;
int deltax = node.col - endNode.col;
return (int)(D*Math.sqrt((deltay*deltay + deltax*deltax)));
}
public boolean findPath()...{
insertIntoOpen(startNode);
while(!open.isEmpty())...{
Node node = open.remove(0);
System.out.println("Scan open");
result[node.row][node.col] = EVEN_FIND;
if(node.equals(endNode))...{
markEven(node);
createPath();
return true;
}
else...{
List neighborNodeList = findNeighbor(node);
Iterator<Node> it = neighborNodeList.iterator();
while(it.hasNext())...{
Node neighborNode = it.next();
if(close.contains(neighborNode))...{
continue;
}
else...{//FIXME 效率超低
if(! open.contains(neighborNode))...{
neighborNode.g = node.g + neighborNode.cost;
neighborNode.f = neighborNode.g + estimateH(neighborNode);
neighborNode.parent = node;
insertIntoOpen(neighborNode);
}
else...{
if(node.g + neighborNode.cost + estimateH(neighborNode) < neighborNode.f)...{
neighborNode.g = node.g + neighborNode.cost;
neighborNode.f = neighborNode.g + estimateH(neighborNode);
open.remove(neighborNode);
neighborNode.parent = node;
insertIntoOpen(neighborNode);
}
}
}
}
}
close.add(node);
}
return false;
}
private void markEven(Node node)...{
result[node.row][node.col] = -1;
}
private void createPath()...{
LinkedList pathlist = new LinkedList();
Node tmp = endNode;
while(tmp.parent != null)...{
result[tmp.row][tmp.col] = SUC_FIND;
tmp = tmp.parent;
}
}
private List findNeighbor(Node node)...{
List<Node> list = new ArrayList();
int left = node.col-1;
int up = node.row-1;
int down = node.row+1;
int right = node.col+1;
if(left > 0)...{
if(grid[node.row][left].cost != BARRIER)
list.add(grid[node.row][left]);
if(up>0)...{
if(grid[up][node.col].cost != BARRIER)
list.add(grid[up][node.col]);
if(grid[up][left].cost != BARRIER)
list.add(grid[up][left]);
}
if(down < gridRows - 1)...{
if(grid[down][node.col].cost != BARRIER)
list.add(grid[down][node.col]);
if(grid[down][left].cost != BARRIER)
list.add(grid[down][left]);
}
}
if(right < gridCols - 1)...{
if(grid[node.row][right].cost != BARRIER)
list.add(grid[node.row][right]);
if(up>0)...{
if(grid[up][right].cost != BARRIER)
list.add(grid[up][right]);
}
if( down < gridRows - 1)...{
if(grid[down][right].cost != BARRIER)
list.add(grid[down][right]);
}
}
return list;
}
public int[][] getResult()...{
return result;
}
private void insertIntoOpen(Node node)...{
int size = open.size();
Iterator<Node>it = open.iterator();
int i = 0;
while(it.hasNext())...{
if(it.next().f > node.f)break;
i++;
}
open.add(i, node);
System.out.println("OPEN ADD NODE:("+ node.row+","+node.col+")" );
}
}
GridValue.java
public enum GridValue...{
BARRIER(-3),
START(-1),
END(-2),
LEN1(1),
LEN10(10),
LEN50(50),
LEN100(100),
LEN150(150);
private int value;
private GridValue(int value)...{
this.value = value;
}
public int toInt()...{
return this.value;
}
}
MainForm.java(界面展示)
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.WindowConstants;
import sun.font.GlyphLayout.GVData;
/** *//**
* This code was edited or generated using CloudGarden's Jigloo
* SWT/Swing GUI Builder, which is free for non-commercial
* use. If Jigloo is being used commercially (ie, by a corporation,
* company or business for any purpose whatever) then you
* should purchase a license for each developer using Jigloo.
* Please visit www.cloudgarden.com for details.
* Use of Jigloo implies acceptance of these licensing terms.
* A COMMERCIAL LICENSE HAS NOT BEEN PURCHASED FOR
* THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED
* LEGALLY FOR ANY CORPORATE OR COMMERCIAL PURPOSE.
*/
public class MainForm extends javax.swing.JFrame ...{
private JSplitPane jSplitPane1;
private JButton jButton3;
private JButton jButton2;
private JButton jButton1;
private JPanel jPanel17;
private JPanel jPanel1;
private JLabel jLabel3;
private JLabel jLabel4;
private JLabel jLabel5;
private JPanel jPanel1Start;
private JLabel jLabel7;
private JPanel jPanel1End;
private JPanel jPanel14;
private JPanel jPanel16;
private JLabel jLabel6;
private JPanel jPanel12;
private JPanel jPanel1Barrier;
private JPanel jPanel10;
private JPanel jPanelLength150;
private JPanel jPanel8;
private JPanel jPanelLength100;
private JPanel jPanel6;
private JLabel jLabel2;
private JPanel jPanelLength50;
private JPanel jPanel4;
private JLabel jLabel1;
private JPanel jPanelLength10;
private GridPanel gridPanel;
private JPanel jPanel2;
/** *//**
* Auto-generated main method to display this JFrame
*/
public static void main(String[] args) ...{
MainForm inst = new MainForm();
inst.setVisible(true);
inst.setTitle("A*算法演示");
}
public MainForm() ...{
super();
initGUI();
}
private void initGUI() ...{
try ...{
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
...{
jSplitPane1 = new JSplitPane();
getContentPane().add(jSplitPane1, BorderLayout.CENTER);
jSplitPane1.setDividerSize(1);
jSplitPane1.setDividerLocation(412);
jSplitPane1.setResizeWeight(1.0);
...{
gridPanel = new GridPanel();
gridPanel.setBackground(Color.BLACK);
gridPanel.setPreferredSize(new Dimension(411,411));
jSplitPane1.setLeftComponent(gridPanel);
// gridPanel.addMouseListener(new )
}
...{
jPanel2 = new JPanel();
FlowLayout jPanel2Layout = new FlowLayout();
jPanel2Layout.setAlignment(FlowLayout.LEFT);
jSplitPane1.setRightComponent(jPanel2);
jPanel2.setBackground(Color.GRAY);
jPanel2.setPreferredSize(new Dimension(40,400));
jPanel2.setLayout(jPanel2Layout);
...{
jPanel1 = new JPanel();
FlowLayout jPanel1Layout = new FlowLayout();
jPanel1Layout.setAlignment(FlowLayout.LEFT);
jPanel1.setLayout(jPanel1Layout);
jPanel2.add(jPanel1);
jPanel1.setPreferredSize(new java.awt.Dimension(165,26));
jPanel1.setBackground(new java.awt.Color(128,128,128));
...{
jPanelLength10 = new JPanel();
jPanel1.add(jPanelLength10);
GridLayout jPanel3Layout = new GridLayout(1, 1);
jPanel3Layout.setHgap(5);
jPanel3Layout.setVgap(5);
jPanel3Layout.setColumns(1);
jPanelLength10.setLayout(jPanel3Layout);
jPanelLength10.setPreferredSize(new java.awt.Dimension(15, 13));
jPanelLength10.setBackground(new java.awt.Color(33,233,0));
jPanelLength10.addMouseListener(new MouseAdapter() ...{
public void mousePressed(MouseEvent evt) ...{
gridPanel.setCurrentValue(GridValue.LEN10);
}
});
}
...{
jLabel1 = new JLabel();
jPanel1.add(jLabel1);
jLabel1.setText("10");
}
}
...{
jPanel4 = new JPanel();
FlowLayout jPanel4Layout = new FlowLayout();
jPanel4Layout.setAlignment(FlowLayout.LEFT);
jPanel4.setLayout(jPanel4Layout);
jPanel2.add(jPanel4);
jPanel4.setPreferredSize(new java.awt.Dimension(165,26));
jPanel4.setBackground(new java.awt.Color(128,128,128));
...{
jPanelLength50 = new JPanel();
GridLayout jPanel5Layout = new GridLayout(1, 1);
jPanel5Layout.setHgap(5);
jPanel5Layout.setVgap(5);
jPanel5Layout.setColumns(1);
jPanel4.add(jPanelLength50);
jPanelLength50.setBackground(new java.awt.Color(255,128,128));
jPanelLength50.setPreferredSize(new java.awt.Dimension(15,13));
jPanelLength50.setLayout(jPanel5Layout);
jPanelLength50.addMouseListener(new MouseAdapter() ...{
public void mousePressed(MouseEvent evt) ...{
gridPanel.setCurrentValue(GridValue.LEN50);
}
});
}
...{
jLabel2 = new JLabel();
jPanel4.add(jLabel2);
jLabel2.setText("50");
}
}
...{
jPanel6 = new JPanel();
jPanel2.add(jPanel6);
FlowLayout jPanel6Layout = new FlowLayout();
jPanel6Layout.setAlignment(FlowLayout.LEFT);
jPanel6.setLayout(jPanel6Layout);
jPanel6.setPreferredSize(new java.awt.Dimension(165,26));
jPanel6.setBackground(new java.awt.Color(128,128,128));
...{
jPanelLength100 = new JPanel();
GridLayout jPanel7Layout = new GridLayout(1, 1);
jPanel7Layout.setHgap(5);
jPanel7Layout.setVgap(5);
jPanel7Layout.setColumns(1);
jPanel6.add(jPanelLength100);
jPanelLength100.setBackground(new java.awt.Color(255,255,128));
jPanelLength100.setPreferredSize(new java.awt.Dimension(15,13));
jPanelLength100.setLayout(jPanel7Layout);
jPanelLength100
.addMouseListener(new MouseAdapter() ...{
public void mousePressed(MouseEvent evt) ...{
gridPanel.setCurrentValue(GridValue.LEN100);
}
});
}
...{
jLabel3 = new JLabel();
jPanel6.add(jLabel3);
jLabel3.setText("100");
}
}
...{
jPanel8 = new JPanel();
FlowLayout jPanel8Layout = new FlowLayout();
jPanel8Layout.setAlignment(FlowLayout.LEFT);
jPanel8.setLayout(jPanel8Layout);
jPanel2.add(jPanel8);
jPanel8.setPreferredSize(new java.awt.Dimension(165,26));
jPanel8.setBackground(new java.awt.Color(128,128,128));
...{
jPanelLength150 = new JPanel();
GridLayout jPanel9Layout = new GridLayout(1, 1);
jPanel9Layout.setHgap(5);
jPanel9Layout.setVgap(5);
jPanel9Layout.setColumns(1);
jPanel8.add(jPanelLength150);
jPanelLength150.setBackground(new java.awt.Color(128,255,128));
jPanelLength150.setPreferredSize(new java.awt.Dimension(15,13));
jPanelLength150.setLayout(jPanel9Layout);
jPanelLength150
.addMouseListener(new MouseAdapter() ...{
public void mousePressed(MouseEvent evt) ...{
gridPanel.setCurrentValue(GridValue.LEN150);
}
});
}
...{
jLabel4 = new JLabel();
jPanel8.add(jLabel4);
jLabel4.setText("150");
}
}
...{
jPanel10 = new JPanel();
FlowLayout jPanel10Layout = new FlowLayout();
jPanel10Layout.setAlignment(FlowLayout.LEFT);
jPanel10.setLayout(jPanel10Layout);
jPanel2.add(jPanel10);
jPanel10.setPreferredSize(new java.awt.Dimension(165,26));
jPanel10.setBackground(new java.awt.Color(128,128,128));
...{
jPanel1Barrier = new JPanel();
GridLayout jPanel11Layout = new GridLayout(1, 1);
jPanel11Layout.setHgap(5);
jPanel11Layout.setVgap(5);
jPanel11Layout.setColumns(1);
jPanel10.add(jPanel1Barrier);
jPanel1Barrier.setBackground(new java.awt.Color(128,255,255));
jPanel1Barrier.setPreferredSize(new java.awt.Dimension(15,13));
jPanel1Barrier.setLayout(jPanel11Layout);
jPanel1Barrier.addMouseListener(new MouseAdapter() ...{
public void mousePressed(MouseEvent evt) ...{
gridPanel.setCurrentValue(GridValue.BARRIER);
}
});
}
...{
jLabel5 = new JLabel();
jPanel10.add(jLabel5);
jLabel5.setText("固體");
}
}
...{
jPanel12 = new JPanel();
FlowLayout jPanel12Layout = new FlowLayout();
jPanel12Layout.setAlignment(FlowLayout.LEFT);
jPanel12.setLayout(jPanel12Layout);
jPanel2.add(jPanel12);
jPanel12.setPreferredSize(new java.awt.Dimension(165,26));
jPanel12.setBackground(new java.awt.Color(128,128,128));
...{
jPanel1Start = new JPanel();
GridLayout jPanel13Layout = new GridLayout(1, 1);
jPanel13Layout.setHgap(5);
jPanel13Layout.setVgap(5);
jPanel13Layout.setColumns(1);
jPanel12.add(jPanel1Start);
jPanel1Start.setBackground(new java.awt.Color(255,0,255));
jPanel1Start.setPreferredSize(new java.awt.Dimension(15,13));
jPanel1Start.setLayout(jPanel13Layout);
jPanel1Start.addMouseListener(new MouseAdapter() ...{
public void mouseClicked(MouseEvent evt) ...{
gridPanel.setCurrentValue(GridValue.START);
}
});
}
...{
jLabel6 = new JLabel();
jPanel12.add(jLabel6);
jLabel6.setText("起點");
}
}
...{
jPanel16 = new JPanel();
jPanel2.add(jPanel16);
jPanel16.setBackground(new java.awt.Color(128,128,128));
jPanel16.setPreferredSize(new java.awt.Dimension(165,26));
...{
jPanel14 = new JPanel();
FlowLayout jPanel14Layout = new FlowLayout();
jPanel14Layout.setAlignment(FlowLayout.LEFT);
jPanel14.setLayout(jPanel14Layout);
jPanel16.add(jPanel14);
jPanel14.setBackground(new java.awt.Color(
128,
128,
128));
jPanel14.setPreferredSize(new java.awt.Dimension(
165,
26));
...{
jPanel1End = new JPanel();
GridLayout jPanel15Layout = new GridLayout(1, 1);
jPanel15Layout.setHgap(5);
jPanel15Layout.setVgap(5);
jPanel15Layout.setColumns(1);
jPanel14.add(jPanel1End);
jPanel1End.setBackground(new java.awt.Color(128,0,255));
jPanel1End.setPreferredSize(new java.awt.Dimension(15,13));
jPanel1End.setLayout(jPanel15Layout);
jPanel1End.addMouseListener(new MouseAdapter() ...{
public void mouseClicked(MouseEvent evt) ...{
gridPanel.setCurrentValue(GridValue.END);
}
});
}
...{
jLabel7 = new JLabel();
jPanel14.add(jLabel7);
jLabel7.setText("終點");
}
}
}
...{
jPanel17 = new JPanel();
GridBagLayout jPanel17Layout = new GridBagLayout();
jPanel17Layout.rowWeights = new double[] ...{0.1, 0.0, 0.0};
jPanel17Layout.rowHeights = new int[] ...{7, 35, 35};
jPanel17Layout.columnWeights = new double[] ...{0.1};
jPanel17Layout.columnWidths = new int[] ...{7};
jPanel17.setLayout(jPanel17Layout);
jPanel2.add(jPanel17);
jPanel17.setPreferredSize(new java.awt.Dimension(170, 197));
jPanel17.setBackground(new java.awt.Color(128,128,128));
...{
jButton1 = new JButton();
jPanel17.add(jButton1, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
jButton1.setText("計算");
jButton1.setPreferredSize(new java.awt.Dimension(130, 25));
jButton1.addActionListener(new ActionListener() ...{
public void actionPerformed(ActionEvent evt) ...{
Arithmetic.AStar astar = new Arithmetic.AStar(gridPanel.getGrid());
if(
astar.findPath()
)...{
gridPanel.setResult(astar.getResult());
JOptionPane.showConfirmDialog(MainForm.this, "FOUND");
}
else JOptionPane.showConfirmDialog(MainForm.this, "NOT FOUND");
gridPanel.repaint();
}
});
}
...{
jButton2 = new JButton();
jPanel17.add(jButton2, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
jButton2.setText("消除路徑");
jButton2.setPreferredSize(new java.awt.Dimension(130, 25));
jButton2.addMouseListener(new MouseAdapter() ...{
public void mouseClicked(MouseEvent evt) ...{
System.out
.println("jButton2.mouseClicked, event="
+ evt);
//TODO add your code for jButton2.mouseClicked
}
});
}
...{
jButton3 = new JButton();
jPanel17.add(jButton3, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
jButton3.setText("清空所有");
jButton3.setPreferredSize(new java.awt.Dimension(130,25));
jButton3.addMouseListener(new MouseAdapter() ...{
public void mouseClicked(MouseEvent evt) ...{
gridPanel.clearAll();
gridPanel.setResult(null);
gridPanel.repaint();
}
});
}
}
}
}
pack();
this.setSize(600, 450);
//this.setExtendedState(JFrame.MAXIMIZED_BOTH);
} catch (Exception e) ...{
e.printStackTrace();
}
}
@Override
public void paint(Graphics g) ...{
// TODO 自動生成方法存根
super.paint(g);
}
}
class GridPanel extends JPanel...{
final int rows = 20;
final int cols = 20;
GridValue [][]grid = new GridValue[rows][cols];
final int gridPanelWidth = 411;
final int gridPanelHeight = 411;
int [][]result;
int rowspan = (gridPanelHeight - 10 - 1) / rows;
int colspan = (gridPanelWidth - 10 - 1) / cols;
int edge = 5;
private GridValue currentValue = GridValue.LEN1;
public GridPanel() ...{
for(int i = 0; i < rows; i++)
for(int j = 0; j < cols;j++)...{
grid[i][j] = GridValue.LEN1;
}
addMouseListener(new MouseAdapter()...{
@Override
public void mousePressed(MouseEvent e) ...{
int xgrid = (e.getX() - edge) / colspan;
int ygrid = (e.getY() - edge) / rowspan;
grid[ygrid][xgrid] = currentValue;
repaint();
}
});
}
public GridValue[][] getGrid()...{
return grid;
}
public void setResult(int[][]result)...{
this.result = result;
}
public void clearAll()...{
for(int i = 0; i < rows; i++)...{
for(int j = 0; j < cols;j++)...{
grid[i][j] = GridValue.LEN1;
}
}
repaint();
}
public void setCurrentValue(GridValue value)...{
currentValue = value;
}
public void drawGrid(Graphics g,int row,int col)...{
//grid[row][col] = value;
int startrow = edge + rowspan*row;
int startcol = edge + colspan*col;
boolean draw = true;
switch(grid[row][col])...{
case BARRIER:
g.setColor(new java.awt.Color(128,255,255));
break;
case START:
g.setColor(new java.awt.Color(255,0,255));
break;
case END:
g.setColor(new java.awt.Color(128,0,255));
break;
case LEN1:
draw = false;
break;
case LEN10:
g.setColor(new java.awt.Color(33,233,0));
break;
case LEN50:
g.setColor(new java.awt.Color(255,128,128));
break;
case LEN100:
g.setColor(new java.awt.Color(255,255,128));
break;
case LEN150:
g.setColor(new java.awt.Color(128,255,128));
break;
default:break;
}
if(draw)
g.fillRect(startcol + colspan/4, startrow+rowspan/4, rowspan/2, colspan/2);
}
public void drawResult(Graphics g,int row,int col)...{
if(result == null)return;
int startrow = edge + rowspan*row;
int startcol = edge + colspan*col;
int r = result[row][col];
boolean draw = true;
if(r == 0)return;
if(r == 1)g.setColor(Color.WHITE);
if(r == 2)g.setColor(Color.YELLOW);
g.drawOval(startcol + colspan/2, startrow+rowspan/2, colspan/4, rowspan/4);
}
@Override
public void paint(Graphics g) ...{
super.paint(g);
g.setColor(Color.WHITE);
for(int i = 0;i < rows+1;i++)...{
g.drawLine(edge, edge + rowspan*i, gridPanelWidth - edge - 1, edge + rowspan*i);
}
for(int i = 0;i < cols+1;i++)...{
g.drawLine( edge + colspan*i, edge, edge + colspan*i, gridPanelHeight - edge - 1);
}
for(int i = 0; i < rows; i++)...{
for(int j = 0; j < cols;j++)...{
drawGrid(g, i, j);
drawResult(g,i,j);
}
}
}
}