您的位置:首页 > 编程语言 > Java开发

十五数码之宽度优先搜索

2017-04-16 16:48 218 查看
在用宽度优先搜索写了八数码之后,又想着写一下15数码,在八数码的基础上做了一些更改,比如字符数组的截取,还有内存的大小更改,但是还是不尽人意,给大家看看吧,希望大家能给些意见,准备用A星算法重新写一下十五数码,看看有什么效果。。。。

算法部分:

import java.util.*;

public class Fifteennumberpath {
final static int dx[] = {-1, 1, 0, 0};
final static int dy[] = { 0, 0,-1, 1};
final static String dir = "UDLR";
static int maxstate =40000000;
static int [][]st = new int[maxstate][16];
static int []goal = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};
static int []dist = new int[maxstate];
static int []fa = new int[maxstate];
static int []move = new int[maxstate];
static boolean []vis = new boolean[maxstate];
static int []fact = new int[16];
static StringBuffer path;
public static boolean isok(int []a) //判断数组中的逆序数,如果为偶数返回true,否则返回false;
{
int sum=0;
for(int i=0; i < 16; i++)
for(int j=i+1; j < 16; j++)
if(a[j] != 0 && a[i] != 0 && a[i] > a[j])
sum++;
if(sum % 2 == 0) {
return true;
}
return false;
}
private static void init_lookup_table()//将数组fact初始化为1,1,2,6,24,120,720,5080,40640
{
fact[0] = 1;
for(int i = 1; i < 16; i++) {
fact[i] = fact[i-1] * i;
}
Arrays.fill(vis, false);//将数组fill中的元素全部用FALSE来填充,也是一个初始化
}
private static boolean try_to_insert(int s) {
int code = 0;
for(int i = 0; i < 16; i++) {
int cnt = 0;
for(int j = i+1; j < 16; j++) {
if(st[s][j] < st[s][i]) {
cnt++;//判断逆序数的数量,从S行的第一个元素到最后一个元素分别比较,
}
}
code += fact[15-i] * cnt;//S行的第一个元素与fact数组第一个的积的和相加,交叉积之和
}
if(vis
) {
return false;//判断vis[code]的值为真的话返回FALSE
}
return vis[code] = true;//判断vis[code]的值不为真的话返回true
}
private static void print_path(int cur) {
while(cur != 1) {
path.insert(0,dir.charAt(move[cur]));//在索引0的位置插入UDRL中一个,由输入的cur来决定
cur = fa[cur];//然后给cur赋值fa数组中索引0的数
}
}
private static int bfs() {  //宽度优先搜索
init_lookup_table(); //将数组fact初始化为1,1,2,6,24,120,720,5080,40640
int front = 1 , rear = 2;
try_to_insert(front);//判断st[front][]这一行中对应的逆序数和
while(front < rear) {
if(Arrays.equals(st[front], goal)) //如果st[front]这一行与goal相等,返回front
{
return front;
}
int z;
for(z = 0; z < 16; z++)
{
if(st[front][z] == 0)
{
break;//当st[front]中出现某个元素为0,跳出,z标记为开始为0的下标
}
}
int x = z/4, y = z%4;
for(int d = 0; d < 4; d++) {
int newx = x + dx[d];
int newy = y + dy[d];
int newz = newx * 4 + newy;
if(newx >= 0 && newx < 4&& newy >= 0 && newy < 4) {
st[rear] = Arrays.copyOf(st[front], st[front].length);
st[rear][newz] = st[front][z];
st[rear][z] = st[front][newz];
dist[rear] = dist[front] + 1;
if(try_to_insert(rear)) {
fa[rear] = front;
move[rear] = d;
rear++;
}
}
}
front++;
}
return 0;
}
public static String solve(String state[])
{
path = new StringBuffer();
for(int i = 0; i < state.length; i++)
{
st[1][i] = Integer.valueOf(state[i]);
System.out.println(state[i]);
}
int ans = bfs();
print_path(ans);
return path.toString();
}
}


UI界面部分:

[code]import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.nio.file.Path;
import java.util.*;

public class FifteennumberFram extends Frame implements ActionListener,KeyListener
{
MenuBar menubar=new MenuBar();//创建新的菜单栏
//Menu menu_file = new Menu("文件(F)");//创建下拉菜单文件
//Menu menu_file1 = new Menu("文件(F)");
Button restart = new Button("随机打乱");//在下拉菜单中设置选项
Button nextPath = new Button("提示");
Button printPath = new Button("开始");
Button exit = new Button("退出程序");
Button[] button;//创建按钮
Panel panel;//创建面板panel
Panel panel1;
int row,col;//设置行数和列数
private static int position,cellNum;//用来保存位置和行列乘积数量的变量
final int dr[] = { 0,-1, 0, 1};
final int dc[] = {-1, 0, 1, 0};
public FifteennumberFram(int row,int col) {
super.setMenuBar(menubar);//Windows风格
try
{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}
catch (Exception e)
{
e.printStackTrace();
}
this.row = row;
this.col = col;
cellNum = row*col;
restart.addActionListener(this);
exit.addActionListener(this);
nextPath.addActionListener(this);
printPath.addActionListener(this);
//menu_file.add(restart);//将选项添加到菜单中
//menu_file1.add(restart);
//menu_file.add(printPath);
//menu_file.add(nextPath);
//menu_file.add(exit);
//menubar.add(menu_file);//将菜单添加到菜单栏中

panel = new Panel(new GridLayout(row,col)) ;
panel1 = new Panel(new GridLayout(1,4)) ;
panel1.add(restart);
panel1.add(nextPath);
panel1.add(printPath);
panel1.add(exit);
button = new Button[cellNum];//创建按钮数目,9个
for(int i = 0; i < cellNum; i++) {
if(i == cellNum - 1) {//初始化按钮上的数字
button[i] = new Button(" ");//最后一个为空
}else {
button[i] = new Button(String.valueOf(i + 1));//其他按钮为对应的数字
}
/*button[0]=new Button("1");
button[1]=new Button("2");
button[2]=new Button("3");
button[3]=new Button("8");
button[4]=new Button(" ");
button[5]=new Button("4");
button[6]=new Button("7");
button[7]=new Button("6");
button[8]=new Button("5");*/
button[i].setFont(new Font("宋体", 1, 20));//设置按钮上显示的数字的字体
button[i].addActionListener(this);//添加监听
button[i].addKeyListener(this);//添加响应
panel.add(button[i]);//在面板上添加按钮
}

4000
position = cellNum - 1;
this.add(BorderLayout.NORTH,panel1);
this.add(BorderLayout.CENTER,panel);//面板排列方式
this.setTitle("十五数码");//文件设置标题
this.setVisible(true);//可显示,必须
this.setSize(300,300);//设置页面的大小
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenWidth = screenSize.width/2;
int screenHeight = screenSize.height/2;
int height = this.getHeight();
int width = this.getWidth();
this.setLocation(screenWidth-width/2, screenHeight-height/2);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
void start()//对于按钮上的数字进行随机排列
{
int a[] = new int[16];
do {
int k = 0;
Random random=new Random();
//调用这个Math.Random()函数能够返回带正号的double值,该值大于等于0.0且小于1.0
Set set=new HashSet();//不允许出现重复元素
while(set.size() < cellNum-1) {
int n=random.nextInt(cellNum-1)+1;
//保证产生的随机数在1-15之间,且不重复,写入set集合中
if(!set.contains(n)) {
set.add(n);
a[k++] = n;
}
}
a[k] = 0;
}while(!Fifteennumberpath.isok(a));
for(int i = 0; i < 16; i++)
button[i].setLabel(String.valueOf(a[i]));
button[cellNum-1].setLabel(" ");
position = cellNum - 1;
}
boolean win()
{
for(int i = 0; i < cellNum - 1; i++)
{
if(button[i].getLabel().equals(" "))//获取每一个按钮的值,如果为空,返回false
{
return false;
}
else if(Integer.valueOf(button[i].getLabel()) != i+1)
//如果对应的按钮与初始化的时候不一致,也返回false
{
return false;
}
/*if(Integer.valueOf(button[0].getLabel()) !=1)
return false;
else if(Integer.valueOf(button[1].getLabel()) !=2)
return false;
else if(Integer.valueOf(button[2].getLabel()) !=3)
return false;
else if(Integer.valueOf(button[3].getLabel()) !=8)
return false;
else if(Integer.valueOf(button[5].getLabel()) !=4)
return false;
else if(Integer.valueOf(button[6].getLabel()) !=7)
return false;
else if(Integer.valueOf(button[7].getLabel()) !=6)
return false;
else if(Integer.valueOf(button[8].getLabel()) !=5)
return false;
else if(button[4].getLabel().equals(" "))
return false;*/
}
return true;//要是与设定的对应的话返回true
}
private boolean judge(Button a, Button b)
//判断按钮a和b之间的位置关系,相邻的话返回true,否则返回false
//(dr,dc)=(0,-1)代表a在b的下方
//(dr,dc)=(-1,0)代表a在b的左方
//(dr,dc)=(0,1)代表a在b的上方
//(dr,dc)=(1,1)代表a在b的右方
{
for(int i = 0; i < 4; i++)
{
if((a.getX()==b.getX()+dr[i]*a.getWidth())&&(a.getY()==b.getY()+dc[i]*a.getHeight()))
{
return true;
}
}
return false;
}

public void actionPerformed(ActionEvent e)
{
String[] state = new String[cellNum];
if(e.getSource() == restart)
{
start();
return;
}
else if(e.getSource() == exit)
{
System.exit(0);
return;
}
else if(e.getSource() == nextPath)
{
for(int i = 0; i < cellNum; i++)
{
if(button[i].getLabel().equals(" "))
{
state[i]="0";
}
else
{
state[i]=button[i].getLabel();
}
}
System.out.println(state);
String path = Fifteennumberpath.solve(state);
//调用写的bfs算法
JOptionPane.showMessageDialog(this,"建议走:"+path+" 一共"+path.length()+"步!");
return;
}
else if(e.getSource() == printPath)
{
for(int i = 0; i < cellNum; i++)
{
if(button[i].getLabel().equals(" "))
{
state[i]="0";
}
else
{
state[i]=button[i].getLabel();
}
}
String path = Fifteennumberpath.solve(state);
for(int i = 0; i < path.length(); i++)
{
switch(path.charAt(i))
{
case 'U':
go(KeyEvent.VK_UP);
break;
case 'D':
go(KeyEvent.VK_DOWN);
break;
case 'L':
go(KeyEvent.VK_LEFT);
break;
case 'R':
go(KeyEvent.VK_RIGHT);
break;
}
try {
Thread.sleep(500);
}
catch (InterruptedException e1)
{
e1.printStackTrace();
}
}
}
for(int i = 0; i < cellNum; i++) {
if(e.getSource() == button[i]) {
if(!button[i].getLabel().equals(" ") && judge(button[i],button[position])) {
button[position].setLabel(button[i].getLabel());
button[i].setLabel(" ");
position = i;
}
}
}
if(win()) {
JOptionPane.showMessageDialog(this,"Congratulations");
}
}
void go(int dir) {
int x = position / col;
int y = position % col;
switch(dir) {
case KeyEvent.VK_UP:
if(x != 0) {
button[position].setLabel(button[position-col].getLabel());
button[position-col].setLabel(" ");
position -= col;
}
break;
case KeyEvent.VK_DOWN:
if(x != row-1) {
button[position].setLabel(button[position+col].getLabel());
button[position+col].setLabel(" ");
position += col;
}
break;
case KeyEvent.VK_LEFT:
if(y != 0) {
button[position].setLabel(button[position-1].getLabel());
button[position-1].setLabel(" ");
position -= 1;
}
break;
case KeyEvent.VK_RIGHT:
if(y != col-1) {
button[position].setLabel(button[position+1].getLabel());
button[position+1].setLabel(" ");
position += 1;
}
break;
}
}
public void keyPressed(KeyEvent e) {
go(e.getKeyCode());
if(win()) {
JOptionPane.showMessageDialog(this,"Congratulations");
//JOptionPane.showMessageDialog(this,this.nextPath);
}
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public static void main(String[] args) {
new FifteennumberFram(4, 4);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息