代价一致搜索java实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37667364/article/details/82795373

上人工智能导论课要求实现这个算法,基本没有找到什么对这个算法的解释,ppt也没说细节,就随便写写了,目前只测试了两个图。

思路:

如上图所示(这里以有向图举例只是为了方便),需要找到从0到3的最短路径:

代价一致的做法,维护一个边缘集,用优先队列存储,步骤:

1.初始边缘集为0的所有后继节点,也就是{1(80),4(90)},()中是到节点的路径长

2.从边缘集合中取出路径值最小的节点A作为扩展节点,如果该扩展节点已经是终点,退出,已经找到最短路径

3.将A从边缘集中移除

4.更新边缘集:原来在里面的节点不管,添加扩展节点的后继节点到边缘集种

6.重复步骤2,3,4

 我们始终选择边缘集中的最小代价的节点先行一步,也就是先扩展它的后继节点,
 其目的是为了使其更快到达终点,因为它有最小代价,最优性,这也是我们需要的。

以上图举例写出边缘集合和扩展节点的更新:

初始edgeSet={1(80),4(99)}

选择1作为扩展节点,更新edgeSet = {4(99),2(80+97)}

选择4作为扩展节点,更新edgeSet={2(80+97),3(99+211)}

选择2作为扩展节点,更新edgeSet={3(80+97+101),3(99+322)}

选择3(80+97+101)作为扩展节点,检测到3就是终点,结束,找到最短路径为0->1->2>3

代码在最下面。

更新:

我想到如果存在(顺时针或者逆时针)的环路,(想多了,实际上无向图就会构成环路),并且环路有着更小的路径花费,会陷入兜圈子,确实是这样子,但是兜圈子过程中路径花费会增大,而兜圈子中又不含终点,所以最终还是会顺利找到结果,新增下面的测试。

运行结果:

因为需要在环路中不断兜圈子,路径长度才能超过11,输出很长,这里不贴出来了
结果:
花费:11,路径:0->3->2

main函数中的2个测试图:

输出:

初始edgeSet:
node,cost:1,80
node,cost:4,99
扩展节点:index,cost:1,80
更新edgeSet后:
node,cost:4,99
node,cost:0,160
node,cost:2,177
扩展节点:index,cost:4,99
更新edgeSet后:
node,cost:0,160
node,cost:2,177
node,cost:0,198
node,cost:3,310
扩展节点:index,cost:0,160
更新edgeSet后:
node,cost:2,177
node,cost:1,240
node,cost:0,198
node,cost:3,310
node,cost:4,259
扩展节点:index,cost:2,177
更新edgeSet后:
node,cost:0,198
node,cost:1,240
node,cost:4,259
node,cost:3,310
node,cost:1,274
node,cost:3,278
扩展节点:index,cost:0,198
更新edgeSet后:
node,cost:1,240
node,cost:1,274
node,cost:4,259
node,cost:3,310
node,cost:3,278
node,cost:1,278
node,cost:4,297
扩展节点:index,cost:1,240
更新edgeSet后:
node,cost:4,259
node,cost:1,274
node,cost:1,278
node,cost:3,310
node,cost:3,278
node,cost:4,297
node,cost:0,320
node,cost:2,337
扩展节点:index,cost:4,259
更新edgeSet后:
node,cost:1,274
node,cost:3,278
node,cost:1,278
node,cost:3,310
node,cost:2,337
node,cost:4,297
node,cost:0,320
node,cost:0,358
node,cost:3,470
扩展节点:index,cost:1,274
更新edgeSet后:
node,cost:1,278
node,cost:3,278
node,cost:4,297
node,cost:3,310
node,cost:2,337
node,cost:3,470
node,cost:0,320
node,cost:0,358
node,cost:0,354
node,cost:2,371
扩展节点:index,cost:1,278
更新edgeSet后:
node,cost:3,278
node,cost:3,310
node,cost:4,297
node,cost:0,354
node,cost:2,337
node,cost:3,470
node,cost:0,320
node,cost:0,358
node,cost:2,371
node,cost:0,358
node,cost:2,375
花费:278,路径:0->1->2->3

图2:

运行结果:

也很长不贴过程了,直接贴结果
花费:3,路径:0->3->4->6

代码如下:

1.EdgeSetElement 


import java.util.PriorityQueue;
//边缘集元素描述
public class EdgeSetElement implements Comparable<EdgeSetElement>{
	//节点的索引,下标
	public int nodeIndex;
	//到达当前节点所需的花费
	public int cost;
	//走过的路径
	public String path;
	
	public EdgeSetElement(int nodeIndex, int cost, String path) {
		this.nodeIndex = nodeIndex;
		this.cost = cost;
		this.path = path;
	}

	@Override
	public int compareTo(EdgeSetElement o) {
		//只要加进来就认为不重复
		//允许存在nodeIndex相同的元素
		if(this.cost < o.cost) {
			return -1;
		} else{
			return 1;
		} 
	}
	
	@Override
	public boolean equals(Object obj) {
		EdgeSetElement e = (EdgeSetElement) obj;
		if(e.nodeIndex == this.nodeIndex && e.cost == this.cost) {
			return true;
		}
		return false;
	}
	
}

2.UniformCostSearch(主要的算法)

package work2;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

/**
 * 代价一致搜索
 * @author outsider
 *
 */
public class UniformCostSearch {
	//边缘集合
	private PriorityQueue<EdgeSetElement> edgeSet = new PriorityQueue<>();
	private int vertexNum;//顶点数
	private int[][] matrix;//无向图
	public UniformCostSearch() {}
	public UniformCostSearch(int vertexNum) {
		this.vertexNum = vertexNum;
		matrix = new int[vertexNum][vertexNum];
	}
	public UniformCostSearch(int vertexNum, int[][] matrix) {
		this.vertexNum = vertexNum;
		this.matrix = matrix;
	}
	/**
	 * 搜索
	 * @param startVertex 起点
	 * @param endVertex 终点
	 * @return
	 */
	public List<EdgeSetElement> search(int startVertex, int endVertex) {
		//优先队列保存边缘集合
		/*PriorityQueue<EdgeSetElement> edgeSet;*/
		//初始化边缘集合
		for(int i = 0; i < vertexNum; i++) {
			if(matrix[startVertex][i] > 0) {
				EdgeSetElement e = new EdgeSetElement(i, matrix[startVertex][i], startVertex+"->"+i);
				edgeSet.add(e);
			}
		}
		System.out.println("初始edgeSet:");
		edgeSet.forEach((edge)->System.out.println("node,cost:"+edge.nodeIndex+","+edge.cost));
		//无法到达终点
		if(edgeSet.size() == 0) {
			return null;
		}
		List<EdgeSetElement> results = new ArrayList<>();
		//需要保存被删除的节点的前一个节点是什么
		while(true) {
			//选择一个边缘集种最小的路径花费值作为扩展节点
			//如果已经是终点的节点被优先队列拿出来说明已经找到最短路径,不需要再寻找
			EdgeSetElement exploded = edgeSet.poll();
			if(exploded.nodeIndex == endVertex) {
				results.add(exploded);
				//可能存在多条路径
				EdgeSetElement node = edgeSet.peek();
				while(node!=null && node.nodeIndex == endVertex&&node.cost == exploded.cost) {
					results.add(edgeSet.poll());
					node = edgeSet.peek();
				}
				return results;
			}
			System.out.println("扩展节点:index,cost:"+exploded.nodeIndex+","+exploded.cost);
			//更新边缘集
			updateEdgeSet(exploded);
			
		}
		
	}
	
	
	/**
	 * 更新边缘集,注意已经是终点的结合
	 * @param exploded 扩展节点
	 */
	public void updateEdgeSet(EdgeSetElement exploded) {
		//找出扩展节点的后继添加到边缘集中
		for(int i = 0; i < vertexNum; i++) {
			if(matrix[exploded.nodeIndex][i] > 0) {
				EdgeSetElement e = new EdgeSetElement(i, exploded.cost + matrix[exploded.nodeIndex][i], exploded.path+"->"+i);
				edgeSet.add(e);
			}
		}
		System.out.println("更新edgeSet后:");
		edgeSet.forEach((edge)->System.out.println("node,cost:"+edge.nodeIndex+","+edge.cost));
	}
	/**
	 * 只是为了方便打印结果
	 * @param start 起点
	 * @param end 终点
	 */
	public void test(int start, int end) {
		List<EdgeSetElement> results = this.search(start, end);
		for(EdgeSetElement result : results) {
			System.out.println("花费:"+result.cost+",路径:"+result.path);
		}
	}
	public static void main(String[] args) {
			System.out.println("测试样例1:ppt上的图");
		int[][] g = new int[5][5];
		g[0][1] = 80;
		g[1][0] = 80;
		g[1][2] = 97;
		g[2][1] = 97;
		g[2][3] = 101;
		g[3][2] = 101;
		g[0][4] = 99;
		g[4][0] = 99;
		g[4][3] = 211;
		g[3][4] = 211;
		UniformCostSearch uniformCostSearch = new UniformCostSearch(5, g);
		uniformCostSearch.test(0, 3);
		
		//测试样例二自己定义一个复杂的图
		System.out.println("\n\n测试样例2:自定义一个图");
		int[][] g2 = new int[8][8];
		g2[0][1] = 4;
		g2[0][3] = 1;
		g2[0][2] = 1;
		g2[3][1] = 1;
		g2[1][4] = 1;
		g2[2][7] = 1;
		g2[2][3] = 1;
		g2[2][5] = 2;
		g2[3][4] = 1;
		g2[4][6] = 1;
		g2[5][6] = 1;
		
		g2[1][0] = 4;
		g2[3][0] = 1;
		g2[2][0] = 1;
		g2[1][3] = 1;
		g2[4][1] = 1;
		g2[7][2] = 1;
		g2[3][2] = 1;
		g2[5][2] = 2;
		g2[4][3] = 1;
		g2[6][4] = 1;
		g2[6][5] = 1;
		UniformCostSearch uniformCostSearch2 = new UniformCostSearch(8, g2);
		uniformCostSearch2.test(0, 6);
		//当出现闭合同向环时将出现死循环
		//测试
		System.out.println("\n\n闭合回路测试:");
		int[][] g3 = new int[4][4];
		g3[0][1] = 1;
		g3[1][2] = 10;
		g3[1][3] = 1;
		g3[3][0] = 1;
		g3[3][2] = 10;
		
		g3[1][0] = 1;
		g3[2][1] = 10;
		g3[3][1] = 1;
		g3[0][3] = 1;
		g3[2][3] = 10;
		UniformCostSearch ucs3 = new UniformCostSearch(4, g3);
		ucs3.test(0, 2);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_37667364/article/details/82795373