当前位置:文档之家› 人工智能实验产生式系统解决汉诺塔(有代码)

人工智能实验产生式系统解决汉诺塔(有代码)

实验一一、实验目的:掌握产生式系统解决汉诺塔算法的基本思想。

二、问题描述:如图所示放置3根柱子,其中一根从上往下按由小到大顺序串有若干个圆盘,要求通过3根柱子移动圆盘。

若规定每次只能移动1片,且不许大盘放在小盘之上,最后要将圆盘从一根柱子移动到另一根柱子上。

三、问题分析及基本思想:汉诺塔(也被称为梵塔)问题有很多解决方法,比较典型的是使用递归算法,而本次设计的算法则是应用人工智能中产生式相关知识进行的求解。

数学模型描述如下:1、设计该问题的状态。

使用了二维数组描述汉诺塔的状态,对n个盘子由大到小分别用数组n、n-1...2、1描述。

例如:当n=4时,二维数组为:1002003004002、定义目标状态。

当n=4时,这里是:001002003004依据如下规则定义产生式规则:1、在移动盘子时,每次只移动A\B\C柱子上可以移动的盘子中最大的盘子。

2、如果上一次已经移动了某个盘子,则下一次不能继续移动,即:一个盘子不能被连续移动两次。

如:某次操作将1号盘子由A柱子移动到B柱子,那么在选择下一个要移动的盘子时应不在考虑1号盘。

3、当某个可以移动的盘子摆放位置不唯一时要将当前状态入栈,并选择盘子移动前所在的柱子的左侧(同理:反方向选择也可)柱子作为移动的目标柱子。

为提高程序运行过程中的空间利用率,产生式规则在汉诺塔移动过程中依据以上规则自动生成。

控制策略依据如下:1、根据以上产生式规则依据,在每次移动盘子时可选择产生式唯一,所以不需要考虑路径选择。

2、当移动的是一组盘子中的最大盘子(即:在要移动的一组盘子中的最下面的盘子)时,观察目标柱子是否是C柱子(最终结果所在柱子),如果是则表示当前盘子移动成功,并清空栈,转移问题(即减小一层盘子);如果移动目标错误(即移动到了A或B柱子)则执行回溯:栈顶状态出栈,向右选择目标柱子产生新的产生式规则,并按此执行移动操作。

3、如果要移动的一组盘子中最大的是1号盘(最后一个盘子),执行的移动操作是将盘子移动到C柱子,则算法结束。

四、主要功能流程图如下:(注意:本设计控制盘子为九个)。

五、源程序:package Hanoi;import java.util.Arrays;import java.util.Scanner;import java.util.Stack;public class Hanoi {public static void main(String[] args) {Scanner in = new Scanner(System.in);System.out.println("请输入汉诺塔盘子的个数:");int n = in.nextInt();// 定义初始状态int[][] first = new int[n][3];System.out.println("初始状态:");for (int i = 0; i < n; i++) {first[i][0] = i + 1;System.out.println(Arrays.toString(first[i]));}// 定义目标状态int[][] end = new int[n][3];for (int i = 0; i < n; i++) {end[i][2] = i + 1;// System.out.println(Arrays.toString(end[i]));}int maxValue = n;// 定义可以移动的最大盘子的值,初值Disk before = null;// 定义上一个移动的盘子,初始值为nullStack<int[][]> stack = new Stack<int[][]>();// 定义存放盘子状态的栈Stack<Disk> bs = new Stack<Disk>();b: while (true) {System.out.println("before:" + before);Disk canMoveDisk = Hanoi.findMoveDisk(first, before);// 找出当前可移动盘子// System.out.println("canMoveDisk:" + canMoveDisk);Disk[] movePlace = Hanoi.movePlace(first,Hanoi.findMoveDisk(first, before));// 返回当前可移动去的位置数组// System.out.println("locateionNumber:" +movePlace.length);Disk right = movePlace[0];// 如果可移动位置有两个,right 为右侧位置Disk left = movePlace[0];// 如果可移动位置有两个,left为左侧位置if (movePlace.length == 2) {if(movePlace[0].y==(canMoveDisk.y+1)%3){right=movePlace[0];left=movePlace[1];}else if(movePlace[0].y==(canMoveDisk.y+2)%3){right=movePlace[1];left=movePlace[0];}int[][] a = new int[n][3];for (int i = 0; i < n; i++)for (int j = 0; j < 3; j++)a[i][j] = first[i][j];stack.push(a);// 把当前状态入栈if (before == null)bs.push(null);else {Disk x = new Disk(before.x, before.y);bs.push(x);}System.out.println("入栈");}// System.out.println("right:" + right);Hanoi.move(canMoveDisk, right, first);before = right;if (maxValue == 1 && Hanoi.findDiskPrice(right, first) == 1&& right.y == 2)break b;if (Hanoi.findDiskPrice(right, first) == maxValue) {// 如果移动了要移动的最大盘子// 如果是则判断目标柱子是否是最终结果所在柱子if (right.y == 2) {// 如果是// 清空栈while(!stack.isEmpty()){stack.pop();}while(!bs.isEmpty()){bs.pop();}System.out.println("清空栈");maxValue =maxValue-1;// 要移动的最大盘子减1} else {for (int i = 0; i < n; i++)for (int j = 0; j < 3; j++)first[i][j] = stack.peek()[i][j];stack.pop();// 出栈恢复System.out.println("出栈恢复");Hanoi.print(first);Disk x = null;if (bs.peek() != null)x = new Disk(bs.peek().x, bs.peek().y);Disk canMoveDisk2 = Hanoi.findMoveDisk(first, x);bs.pop();Disk[] movePlace2 = Hanoi.movePlace(first, canMoveDisk2);// System.out.println("canMoveDisk:" + canMoveDisk2);// System.out.println("locateionNumber:" + movePlace2.length);Disk r = movePlace2[0];// 如果可移动位置有两个,right为右侧位置Disk l = movePlace2[0];// 如果可移动位置有两个,left为左侧位置if (movePlace2.length == 2) {if(movePlace2[0].y==(canMoveDisk2.y+1)%3){r=movePlace2[0];l=movePlace2[1];}elseif(movePlace2[0].y==(canMoveDisk2.y+2)%3){r=movePlace2[1];l=movePlace2[0];}}Hanoi.move(canMoveDisk2, l, first);// 选择左侧位置移动盘子// System.out.println("canMoveDisk:" + canMoveDisk2);// System.out.println("locateionNumber:" + movePlace.length);// System.out.println("left:" + l);before = l;if (maxValue == 1 && Hanoi.findDiskPrice(left, first) == 1&& l.y == 2)break b;}}}}// 找出可移动的盘子private static Disk findMoveDisk(int[][] a, Disk before) { Disk[] d = new Disk[3];// 柱子1,2,3的顶部盘子for (int i = 0; i < 3; i++) {d[i] = Hanoi.findTopDisk(i + 1, a);}for (int i = 0; i < 3; i++) {if (before == null)return d[0];else if ((!before.equals(d[i]))&& Hanoi.findDiskPrice(d[i], a) != 0&& ((Hanoi.findDiskPrice(d[i], a) <Hanoi.findDiskPrice(d[(i + 1) % 3], a)|| Hanoi.findDiskPrice(d[i], a) < Hanoi.findDiskPrice(d[(i + 2) % 3], a)|| Hanoi.findDiskPrice(d[(i + 1) % 3], a) == 0 || Hanoi.findDiskPrice(d[(i + 2) % 3], a) == 0))) // System.out.println(d[i]);return d[i];}return null;}// 找出可以移动的位置坐标private static Disk[] movePlace(int[][] a, Disk canMoveDisk) {Disk[] d = new Disk[2];int h = 0;// h是数组Disk的下标int i = canMoveDisk.y;// 当前可移动盘子的柱子纵坐标Disk d1 = findTopDisk((i + 1) % 3 + 1, a);Disk d2 = findTopDisk((i + 2) % 3 + 1, a);if (Hanoi.findDiskPrice(d1, a) == 0) {d[h] = d1;h++;} else if ((Hanoi.findDiskPrice(canMoveDisk, a) <Hanoi.findDiskPrice(d1, a))) {if (d1.x != 0) {Disk d0 = new Disk(d1.x - 1, d1.y);d[h] = d0;h++;}}if (Hanoi.findDiskPrice(d2, a) == 0) {d[h] = d2;h++;} else if ((Hanoi.findDiskPrice(canMoveDisk, a) < Hanoi.findDiskPrice(d2, a))) {if (d2.x != 0) {Disk d0 = new Disk(d2.x - 1, d2.y);d[h] = d0;h++;}}Disk[] d3;if(d[1]!=null){d3 = new Disk[2];d3[0] = d[0];d3[1] = d[1];}else{d3 = new Disk[1];d3[0] = d[0];}return d3;}// 找出一个Disk对应坐标的盘子值private static int findDiskPrice(Disk d, int[][] a) { return a[d.x][d.y];}// 找出柱子i的顶部盘子private static Disk findTopDisk(int i, int[][] a) { int j;for (j = 0; j < a.length; j++) {if (a[j][i - 1] == 0)continue;else {Disk d = new Disk(j, i - 1);return d;}}if (j >= a.length) {Disk d = new Disk(a.length - 1, i - 1);return d;}return null;}// 移动盘子,将盘子d1移至d2位置private static void move(Disk d1, Disk d2, int[][] a) { System.out.println("将盘子" + Hanoi.findDiskPrice(d1, a) + "从位置" + d1+ "移至位置" + d2 + " " + (d1.y + 1) + "号柱子-->" + (d2.y + 1)+ "号柱子");a[d2.x][d2.y] = a[d1.x][d1.y];a[d1.x][d1.y] = 0;Hanoi.print(a);}// 打印当前盘子位置分布private static void print(int[][] a) {for (int i = 0; i < a.length; i++)System.out.println(Arrays.toString(a[i]));}}package Hanoi;//定义一个盘子的位置class Disk {int x;// 盘子的横坐标int y;// 盘子的纵坐标public Disk(int x, int y) {this.x = x;this.y = y;}public boolean equals(Disk d) {if (this.x == d.x && this.y == d.y)return true;return false;}@Overridepublic String toString() {return"Disk" + "(" + this.x + "," + this.y + ")";}}六、运行结果(9层汉诺塔结果):......一层汉诺塔:二层汉诺塔:三层汉诺塔:七、结果分析:汉诺塔问题通过我们都知道的递归算法来实现很简单,而本实验中采用了产生式系统解决汉诺塔问题,确定要移动的盘子,确定盘子移去的位置都只要根据所给出的规则就能够确定了,整个实现过程看起来很复杂,其实根据给出的产生式规则都可以很好的解决。

相关主题