BFS实现过程
- 定义队列,用于存储每一步的棋盘状态
- 开局寻找到空格的位置,然后以0123的顺序进行遍历,0123分别代表向下,向左,向上,向右.
- 每向一个方向走一步,便将四个到达这一步后的所有方向放进队列,然后再从队列中获取新的情况进行遍历,由于队列先进先出FIFO的特点,所以进行实现了BFS的搜索.
- 如果队列内有重复的情况,便不再进队,经过这样处理能够大幅度减少遍历的过程,而且报证了遍历的时间是有限的,因为棋盘的总情况只有9!种情况.(由于可达性的问题,如果可达,情况很更少一些)
DFS实现过程
1.定义栈,用于存储每一步的棋盘状态 - 开局寻找到空格的位置,然后以0123的顺序进行遍历,0123分别代表向下,向左,向上,向右.
- 如果按照原本的深度优先搜索,会不停的进行压栈处理,因为在八数码难题,树的深度可以是9!,即直到找完所有情况,或者找到解才会停止.同样进行BFS第四步的操作,可以保证最终可以搜索到解,因为情况有限.但是在算法实现中,由于不停的进行压栈却不出栈,会导致计算机内存溢出,无法执行结果.
4.针对3,使用了深度受限的深度搜索,设定长度为30(由于BFS已经搜索出结果,为了更快出结果,所以将深度设置的更浅)
5.但在实际运行中,由于深度优先搜索的会从下往上,而BFS由BFS的结果会知道,DFS结果由于一开始的搜索方向便没有往解的方向搜索,因此运行时间太长,为了减少等待时间,继续对其优化.
6.采用BFS与DFS相结合的方法.将栈定义为双向队列.当BFS搜索的达到限制深度以后,不在出栈,而是出队,即取出第一个进入的元素进行深度优先搜索,因此等待时间变得可接受,当然,当换一种情况以后便不一定起到减少时间的作用.
A*搜索
1.定义一个优先级队列来存储每一步的状态 - 定义f(n)=g(n)+h(n)为初始状态经由状态n到目标状态的代价估计.g(n)为实际代价,在我的程序中即为当前路径长度,g(n)在A*算法中,我采用的是非常简化的估计,不在目标位置的数字数量.在后面的遗传算法中,为了提高遗传算法的效率,计算遗传算法的适应度使用的g(n)是每个数字到目标位置的曼哈顿距离.
3.使用BFS算法进行遍历每个方向,与之不同的是,这是一个优先队列,会对每个入队状态排序,损失越低的排的越前,即越接近目标状态g(n)越小的会越优先遍历.
遗传算法
群体智能算法中,我选用了遗传算法.
1.为了方便算法,遗传算法中,路径换了一种方式进行表达,用0123代表空格向下左上右方向进行移动.
2.在八数码难题中,以路径为基因.本题中,基因长度为100.随机生成0-3的100个数字作为一个染色体.注:如果当空格沿着路径走,碰到边界无法走以后,将会忽略该基因(不表达).
3.我采用的适应度计算方式为150-每个数字的曼哈顿距离-不在正确位置的位置数-正确的最短路径长度.计算过程,从第一个基因开始走路,并计算到当前基因为止的计算适应度,直到走到目标状态,即可能出现基因长度的一半便到达目标状态的情况.
曼哈顿距离:
目标状态 每个位置的编码
012 012
456 456
789 789
曼哈顿距离的计算,每个数字当前位置到目标位置需要移动的步数
4.每个参数的设置,种群个数为100,染色体基因长度为100,交换率为0.95,变异率为0.02精英遗传个体数为5.遗传最大代数为10000.
5.每代采用轮盘赌算法进行亲代选择
(六) 实验结果与分析说明
BFS结果:
12
345
678
[4, 4, 3, 0, 1, 4, 7, 6, 3, 4, 5, 2, 1, 0, 3, 4, 5, 8, 7, 6, 3, 4, 5, 2, 1, 4, 3, 0] 28
由于程序设计原因,第二个数字不算在内,从第三步开始,即3开始,总共26步走到目标状态.
内存为213909504字节 时间为475721ms
DFS结果
12
345
678
[4, 4, 3, 6, 7, 4, 5, 8, 7, 4, 3, 6, 7, 4, 1, 2, 5, 4, 1, 0, 3, 4, 1, 2, 5, 8, 7, 4, 3, 0] 30
解释同BFS,共27步走到目标结果.
内存122558824字节 时间143528ms
A*结果
12
345
678
[4, 4, 3, 0, 1, 4, 7, 6, 3, 4, 5, 2, 1, 0, 3, 4, 5, 8, 7, 6, 3, 4, 5, 2, 1, 4, 3, 0] 28
最终解和BFS搜索相同,即最短路径
内存35115520字节
时间8349ms
说明:
数字表了空格的位置,九宫格每个位置进行了编号,为:
012 724 一开始空格的位置为4, 724
345 初始棋盘为 5 6 [4,3]代表了空格向左走 56
678 831 即: 831
遗传算法
染色体
2333203100303112231113232030031122232011230032122322332333300132130130310123333212012033003003011222
第85步走到目标解
内存使用92917072字节 运行时间3346ms
在截图的实验中,种群的总适应度由初始的2700逐渐上升至4600左右.
分析:
1.关于BFS,由于从浅层开始搜索,当情况的数量有限时,一定能找到解且一定是最优解.缺点是当解深度较大、情况过多时,时间会比较漫长。而且与初始遍历的顺序也会有关系。改进可以考虑使用双向宽度搜索。
2.关于DFS,由于从深层开始搜索,所以搜索到的结果大概率不是最优解,而且与初始遍历的顺序有关,同样的,当情况较多时,搜索时间很漫长。由此演化而来的搜索方法,深度受限搜索。改进方法可以是BFS、DFS相结合进行搜索。
3.遗传算法,遗传算法利用了生物进化的原理,能够在有限时间内搜索出比较好的结果。但是容易陷入局部最优解,但是合理的设置参数可以减少这种情况的发生。在实际使用过程中,一般会将遗传算法同其他算法一同配合使用,如模拟退火算法和遗传算法相结合进行使用。
感悟和收获:
人工智能也是一门有较长历史的学科,有很多和数学交叉的地方。比如在数学建模中,人工智能的算法常常会使用到,或者说人工智能的算法本身也是一种建模过程,在学习人工智能方法的过程中,经常也会在数学建模这门课程见到同一个算法的应用。本学期除了选修了人工智能同样选修了数学建模,在人工智能学习到的算法很快的就能在数学建模中上手应用,而应用的过程又加深了对算法的理解。
关于选用JAVA语言的原因,由于本学期课程正在学习JAVA语言,所以选用JAVA语言其实也是想锻炼一下JAVA语言的编程水平,而且在编程过程中确实踩了很多坑,比如深拷贝和浅拷贝的问题,但由于这是人工智能的实验报告,因此就不太好在实验报告中进行总结了。
本学期也借由这一章学到的几个算法,参加了一个有关资源最优分配的项目设计,使用到了遗传算法以及模拟退火。在本次实验报告编写和项目的参与过程中,开始尝试使用计算机的视角去解决问题,例如BFS和DFS搜索,在我的直观感觉中是暴力的,不具备数学美感的解法,然而或许就是计算机的这种暴力(非常夸张的运算能力),成就了计算机算法之美叭,以上仅为个人一点感悟。
BFS、DFS、A*算法代码
package AI1;
import java.util.*;
public class ChessBoard {
//0往下走,1往左走,2往上走,3往右走
static public Queue<ChessBoard> queue = new LinkedList<>();
static public Deque<ChessBoard> stack = new ArrayDeque<>();
static public Queue<ChessBoard> pqueue = new PriorityQueue<>(new Comparator<ChessBoard>() {
@Override
public int compare(ChessBoard o1, ChessBoard o2) {
return o1.cost-o2.cost;
}
});
public static List<String> been = new ArrayList<>();//存储遍历过的图形
public int flag = 0;
public char[][] chessBoard = new char[3][3];
public int x, y;
public List<Integer> history = new ArrayList<>();
private int cost = 1000;
ChessBoard() {
chessBoard = new char[][]{{'7', '2', '4'},
{'5', ' ', '6'},
{'8', '3', '1'}};
// chessBoard = new char[][]{{' ', '1', '2'}, {'3', '4', '5'}, {'6', '7', '8'}};
findspace();
history.add(x + 3 * y);
history.add(x + 3 * y);
findcost();
}
public ChessBoard(ChessBoard cb) {
for (int i = 0; i < 3; i++) {
for (int i1 = 0; i1 < 3; i1++) {
this.chessBoard[i][i1] = cb.chessBoard[i][i1];
}
}
findspace();
findcost();
for (int t : cb.history) {
history.add(t);
}
}
public void tonext() {
switch (flag) {
case 0:
todown();
flag++;
case 1:
toleft();
flag++;
case 2:
toup();
flag++;
case 3:
toright();
flag++;
}
}
private boolean todown() {
ChessBoard cb = new ChessBoard(this);
if (y != 2 && (((y + 1) * 3 + x) != history.get(history.size() - 2))) {
cb.chessBoard[y][x] = cb.chessBoard[y + 1][x];
cb.chessBoard[y + 1][x] = ' ';
cb.findspace();
cb.findcost();
cb.history.add(cb.x + cb.y * 3);
pqueue.offer(cb);
queue.offer(cb);
stack.push(cb);
return true;
}
return false;
}
private boolean toleft() {
ChessBoard cb = new ChessBoard(this);
if (x != 0 && ((y * 3 + x - 1) != history.get(history.size() - 2))) {
cb.chessBoard[y][x] = cb.chessBoard[y][x - 1];
cb.chessBoard[y][x - 1] = ' ';
cb.findspace();
cb.history.add(cb.x + cb.y * 3);
cb.findcost();
pqueue.offer(cb);
queue.offer(cb);
stack.push(cb);
return true;
}
return false;
}
private boolean toup() {
ChessBoard cb = new ChessBoard(this);
if (y != 0 && (((y - 1) * 3 + x) != history.get(history.size() - 2))) {
cb.chessBoard[y][x] = cb.chessBoard[y - 1][x];
cb.chessBoard[y - 1][x] = ' ';
cb.findspace();
cb.history.add(cb.x + cb.y * 3);
cb.findcost();
pqueue.offer(cb);
stack.push(cb);
queue.offer(cb);
return true;
}
return false;
}
private boolean toright() {
ChessBoard cb = new ChessBoard(this);
if (x != 2 && ((y * 3 + x + 1) != history.get(history.size() - 2))) {
cb.chessBoard[y][x] = cb.chessBoard[y][x + 1];
cb.chessBoard[y][x + 1] = ' ';
cb.findspace();
cb.history.add(cb.x + cb.y * 3);
cb.findcost();
pqueue.offer(cb);
stack.push(cb);
queue.offer(cb);
return true;
}
return false;
}
public void findspace() {
for (int i = 0; i < 3; i++) {
for (int i1 = 0; i1 < 3; i1++) {
if (chessBoard[i][i1] == ' ') {
this.y = i;
this.x = i1;
}
}
}
}
public void findcost(){
int flag=0;
char num = '0';
for (int i = 0; i < 3; i++) {
for (int i1 = 0; i1 < 3; i1++) {
if (i == 0 && i1 == 0) {
if (chessBoard[i][i1] != ' ')
flag++;
} else {
if (chessBoard[i][i1] != num)
flag++;
}
num++;
}
}
cost = flag + history.size();
}
public boolean right() {
char num = '0';
for (int i = 0; i < 3; i++) {
for (int i1 = 0; i1 < 3; i1++) {
if (i == 0 && i1 == 0) {
if (chessBoard[i][i1] != ' ')
return false;
} else {
if (chessBoard[i][i1] != num)
return false;
}
num++;
}
}
return true;
}
public void displaychessBoard() {
for (int i = 0; i < 3; i++) {
System.out.println(chessBoard[i]);
}
System.out.println(history+" "+history.size());
}
static void DFS() {
String s = "";
ChessBoard cb = new ChessBoard();
for (char a[] : cb.chessBoard)
for (char a1 : a)
s += a1;
been.add(s);
s = "";
int i = 0;
while (!cb.right()) {
cb.tonext();
do {
cb = ChessBoard.stack.pop();
// cb.displaychessBoard();
while (cb.history.size() > 30)
cb = ChessBoard.stack.pollLast();
for (char a[] : cb.chessBoard)
for (char a1 : a)
s += a1;
}
while (been.contains(s));
been.add(s);
s = "";
if(i%10000==0) {
cb.displaychessBoard();
i = 0;
}
i++;
}
cb.displaychessBoard();
}
static void BFS() {
String s = "";
ChessBoard cb = new ChessBoard();
for (char a[] : cb.chessBoard)
for (char a1 : a)
s += a1;
been.add(s);
s = "";
int i = 0;
while (!cb.right()) {
cb.tonext();
do {
cb = ChessBoard.queue.poll();
for (char a[] : cb.chessBoard)
for (char a1 : a)
s += a1;
// cb.displaychessBoard();
}
while (been.contains(s));
been.add(s);
s = "";
if(i%10000==0) {
cb.displaychessBoard();
i = 0;
}
i++;
}
cb.displaychessBoard();
}
static void Astar() {
String s = "";
ChessBoard cb = new ChessBoard();
for (char a[] : cb.chessBoard)
for (char a1 : a)
s += a1;
been.add(s);
s = "";
int i = 0;
while (!cb.right()) {
cb.tonext();
do {
cb = ChessBoard.pqueue.poll();
for (char a[] : cb.chessBoard)
for (char a1 : a)
s += a1;
// cb.displaychessBoard();
}
while (been.contains(s));
been.add(s);
s = "";
if(i%10000==0) {
cb.displaychessBoard();
i = 0;
}
i++;
}
cb.displaychessBoard();
}
}
测试类
package AI1;
public class Main {
public static void main(String[] args) {
long startTime=System.currentTimeMillis(); //获取开始时间
ChessBoard.DFS();
long endTime=System.currentTimeMillis(); //获取结束时间
Runtime s_runtime = Runtime.getRuntime();
System.out.println("内存:"+(s_runtime.totalMemory() - s_runtime.freeMemory())+"字节" );
System.out.println("时间:"+(endTime-startTime)+"ms");
}
}
遗传算法代码
package AI1pulsplus;
import java.util.*;
public class ChessBoard {
//个体数100,基因长度为100的种群
Individual[] zhongqun;
int alladaption;
ChessBoard() {
zhongqun = new Individual[100];
for (int i = 0; i < 100; i++) {
zhongqun[i] = new Individual();
}
for (int i = 0; i < 10000; i++) {
this.jinhua();
for (int j = 0; j < 100; j++) {
zhongqun[j].dnashow();
}
alladaption();
if (i % 50 == 0) {
System.out.print(alladaption + " ");
System.out.println();
// for (int q=0;q<100;q++) {
// System.out.print(zhongqun[q].adaption+" ");
// }
// System.out.println();
}
}
System.out.println(alladaption);
for (int i = 0; i < 100; i++) {
System.out.print(zhongqun[i].adaption + " ");
}
// System.out.println();
// for (int i = 0; i < 5; i++) {
// for (int t : zhongqun[i].dna)
// System.out.print(t);
// System.out.println();
// }
int size = Individual.list.size();
Collections.sort(Individual.list);
System.out.println();
System.out.println("adaption:");
for (Individual a:Individual.list)
System.out.print(a.adaption+" ");
System.out.println();
System.out.println("size:"+Individual.list.size());
for (int t : Individual.list.get(size-1).dna)
System.out.print(t);
System.out.println();
System.out.println("location:"+Individual.list.get(size-1).right);
}
void jinhua() {
alladaption();
double temp;
double leiji;
Individual[] newdna = new Individual[100];
int t = 0;
// for (int i = 0; i < 100; i++) {
// newdna[i] = new Individual();
// }
while (t != 100) {
leiji = 0;
temp = Math.random();
Individual[] dna1 = new Individual[100];
int z = 0;
for (int i = 0; i < 100; i++) {
leiji += (zhongqun[i].adaption * 1.0) / (alladaption * 1.0);
if (leiji > temp) {
dna1[z] = new Individual(zhongqun[i]);
// System.out.println(dna1[z]==zhongqun[i]);
z++;
}
}
for (int i = 0; i < z - 1; i++) {
int start = (int) (Math.random() * 101);
if (100 - start < 20)
start = 80;
// for(int q:dna1[i].dna)
// System.out.print(q);
// System.out.println("11111");
cross(dna1[i].dna, dna1[i + 1].dna, start, 20);
newdna[t] = new Individual(dna1[i]);
// System.out.println(newdna[t]==dna1[i]);
// for(int q:dna1[i].dna)
// System.out.print(q);
// System.out.println("11111");
t++;
if (t == 100) break;
}
}
Arrays.sort(zhongqun);
for (int i = 0; i < 5; i++)
newdna[95 + i] = new Individual(zhongqun[95 + i]);
for (int i = 0; i < 100; i++)
zhongqun[i] = new Individual(newdna[i]);
shuffle(zhongqun);
bianyi();
}
public static void shuffle(Individual[] nums) {
Random rnd = new Random();
for (int i = nums.length - 1; i > 0; i--) {
int j = rnd.nextInt(i + 1);
//swap index i, j
Individual t = nums[i];
nums[i] = nums[j];
nums[j] = t;
}
}
void bianyi() {
for (int i = 0; i < 100; i++) {
if (Math.random() > 0.98) {
int start = (int) (Math.random() * 101);
int len = (int) (Math.random() * (100 - start));
for (int t = start; t < start + len; t++) {
zhongqun[i].dna[t] = (int) (Math.random() * 4);
}
}
}
}
void alladaption() {
alladaption = 0;
for (int i = 0; i < 100; i++)
alladaption += zhongqun[i].adaption;
}
void cross(int[] a, int b[], int start, int len) {
for (int i = start; i < start + len; i++) {
int t = a[i];
a[i] = b[i];
b[i] = t;
}
}
}
class Individual implements Comparable {
@Override
public int compareTo(Object o) {
Individual o1 = (Individual) o;
if (this.adaption < o1.adaption)
return -1;
else if (this.adaption > o1.adaption)
return 1;
else
return 0;
}
static int[][] manhattan = {
{0, 1, 2, 1, 2, 3, 2, 3, 4},//0
{1, 0, 1, 2, 1, 2, 3, 2, 3},//1
{2, 1, 0, 3, 2, 1, 4, 3, 2},//2
{1, 2, 3, 0, 1, 2, 1, 2, 3},//3
{2, 1, 2, 1, 0, 1, 2, 1, 2},//4
{3, 2, 1, 2, 1, 0, 3, 2, 1},//5
{2, 3, 4, 1, 2, 3, 0, 1, 2},//6
{3, 2, 3, 2, 1, 2, 1, 0, 1},//7
{4, 3, 2, 3, 2, 1, 2, 1, 0} //8
};//曼哈顿距离
//0往下走,1往左走,2往上走,3往右走
static int[] x = {0, -1, 0, 1};
static int[] y = {1, 0, -1, 0};
public int space_x = 1, space_y = 1;
public int[][] chessBoard = {{7, 2, 4}, {5, 0, 6}, {8, 3, 1}};
// public int[][] chessBoard = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
public int dna[] = new int[100];
public int adaption;
public int right = 100;
static List<Individual>list = new LinkedList<>();
Individual() {
for (int i = 0; i < 100; i++) {
dna[i] = (int) (Math.random() * 4);
}
dnashow();
}
Individual(Individual a) {
for (int i = 0; i < 100; i++)
this.dna[i] = a.dna[i];
this.right = a.right;
this.space_x = a.space_x;
this.space_y = a.space_y;
this.adaption = a.adaption;
this.chessBoard = a.chessBoard;
}
boolean right() {
int q = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (chessBoard[i][j] != q)
return false;
q++;
}
}
return true;
}
void dnashow() {
for (int i = 0; i < 100; i++) {
if (space_x + x[dna[i]] >= 0 && space_x + x[dna[i]] < 3 && space_y + y[dna[i]] >= 0 && space_y + y[dna[i]] < 3) {
int temp = chessBoard[space_y + y[dna[i]]][space_x + x[dna[i]]];
chessBoard[space_y + y[dna[i]]][space_x + x[dna[i]]] = 0;
chessBoard[space_y][space_x] = temp;
space_x += x[dna[i]];
space_y += y[dna[i]];
// displaychessBoard();
}
if (right()) {
// for (int x : dna)
// System.out.print(x);
right = i+1;
// System.out.println("找到了:" + right);
adaption();
list.add(new Individual(this));
// System.out.println();
// displaychessBoard();
}
adaption();
}
chessBoard = new int[][]{{7, 2, 4}, {5, 0, 6}, {8, 3, 1}};
space_x = 1;
space_y = 1;
}
void jianyan(int q) {
for (int i = 0; i < q; i++) {
// System.out.println(dna[i]);
if (space_x + x[dna[i]] >= 0 && space_x + x[dna[i]] < 3 && space_y + y[dna[i]] >= 0 && space_y + y[dna[i]] < 3) {
int temp = chessBoard[space_y + y[dna[i]]][space_x + x[dna[i]]];
chessBoard[space_y + y[dna[i]]][space_x + x[dna[i]]] = 0;
chessBoard[space_y][space_x] = temp;
space_x += x[dna[i]];
space_y += y[dna[i]];
// System.out.println("x="+space_x);
// System.out.println("y="+space_y);
// displaychessBoard();
}
}
displaychessBoard();
}
void adaption() {
adaption = 150;
int flag = 0;
int length = 0;
int num = 0;
for (int i = 0; i < 3; i++) {
for (int i1 = 0; i1 < 3; i1++) {
if (chessBoard[i][i1] != num) {
flag++;
length += manhattan[i * 3 + i1][chessBoard[i][i1]];
}
num++;
}
}
adaption = adaption - (flag + length) - right;
//计算适应度,每个数字的距离原位置的曼哈顿距离加上错位的数字数
// chessBoard =new int[][] {{7, 2, 4}, {5, 0, 6}, {8, 3, 1}};
}
public void displaychessBoard() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
System.out.print(chessBoard[i][j] + " ");
System.out.println();
}
}
}
测试类
package AI1pulsplus;
import java.util.ArrayList;
import java.util.List;
public class MAIN {
public static void main(String[] args) {
long startTime=System.currentTimeMillis(); //获取开始时间
ChessBoard chessBoard =new ChessBoard();
long endTime=System.currentTimeMillis(); //获取结束时间
Runtime s_runtime = Runtime.getRuntime();
System.out.println("内存:"+(s_runtime.totalMemory() - s_runtime.freeMemory())+"字节" );
System.out.println("时间:"+(endTime-startTime)+"ms");
// Individual i =new Individual();
// char a[] = "3011233223112322130300323001011012001231130101001111011023302132101223023011200103333022200113212222".toCharArray();
// int b[] = new int[100];
// for (int q=0;q<100;q++){
// b[q]=a[q]-'0';
// }
// i.dna=b;
// i.jianyan(100);
// for(int q=0;q<100;q++)
// System.out.print(i.dna[q]);
}
}