最短路径:地图软件是如何计算出最优出行路径的?

最短路径:地图软件是如何计算出最优出行路径的?

针对有权图,就是图中的每条边都有一个权重,如何计算两点之间的最短路径(经过的边的权重和最小)呢?从地图软件的路线规划问题,看看最短路径算法

算法剖析

最优问题有三个:最短路线、最少用时和最少红绿灯

最简单的是最短路线,解决软件开发中的实际问题,最重要的是建模,即将复杂的场景抽象成具体的数据结构,该如何抽象成数据结构呢?把地图抽象成图最合适不过了,把每个岔路口都看成一个顶点,岔路口和岔路口之间的路看做一条边,路的长度就是边的权重,如果路是单行道,就在两个顶点之间画一条有向边,如果路是双行道,就在两个顶点之间画两条方向不同的边,这样就可以抽象成一个有向有权图

即一个有向有权图中求两个顶点间的最短路径:

public class Graph{    //有向有权图的邻接表表示
	private LinkedList<Edge> adj[]; //邻接表
	private  int v;   //顶点个数
	
	public Graph(int v ){
		this.v = v;
		this.adj = new LinkedList[v];
		for(int i = 0 ; i < v ; ++i){
			this.adj[i] = new LinkedList<>();
		}
	}
	
	public void addEdge(int s , int  t , int w ){    //添加一条边
		this.adj[s].add( new Edge(s,t,w));
	}
	
	private class Edge{
		public int sid;   //边的起始顶点编号
		public int tid;  //边的终止顶点编号
		public int w ;  // 权重
		public Edge(int sid, int tid ,int w){
			this.sid = sid ;
			this.tid = tid ;
			this.w = w ;
		}
	}
	//下面这个类是为了dijkstra实现用的
	private class Vertex {
		public int id ;  ///顶点编号ID
		public int dist ; //从起始顶点到这个顶点的距离
		public Vertex(int id ,int dist){
			this.id = id ;
			this.dist = dist;
		}
	}
}

最短路径算法也是单源最短路径算法(一个顶点到一个顶点),最出名的是Dijkstra算法

//因为Java提供的优先级队列,没有暴露更新数据的接口,所以要重新实现一个
private class PriorityQueue{   //根据vertex.dist构建小顶堆
	private Vertex[] nodes;
	private int count;
	public PriorityQueue(int v){
		this.nodes = new Vertex[v+1];
		this.count = v;
	}
	public Vertex poll(){……}
	public void add(Vertex vertex){……}
	//更新结点的值,并从下往上堆化,重新符合堆的定义,时间复杂度O(Logn)
	public void update(Vertex vertex){……}
	public boolean isEmpty(){……}
}

public void dijkstra(int s ,int t){   //从顶点s到顶点t的最短路径
	int[] predecessor = new int[this.v]; //用来还原最短路径
	Vertex[] vertexes = new Vertex[this.v];
	for(int i = 0 ; i < this.v ; ++i){
		vertexes[i] = new Vertex( i , Integer.MAX_VALUE);
	}
	PriorityQueue queue = new PriorityQueue(this.v);//小顶堆
	boolean[] inqueue = new boolean[this.v];  //标记是否进入过队列
	vertexes[s].dist = 0 ;
	queue.add(vertexes[s]);
	inqueue[s] = true;
	while(!queue.isEmpty()){
		Vertex minVertex = queue.poll();  //取堆顶元素并删除
		if(minVertex.id == t) break;//最短路径产生
		for(int i = 0 ; i < adj[minVertex.id].size();++i){
			Edge e  = adj[minVertex.id].get(i);  //取出一条minVertex相连的边
			Vertex nextVertex - vertexes[e.tid];  //minVertex -> nextVertex
			if(minVertex.dist + e.w < nextVertex.dist){   //更新next的dist
				nextVertex.dist = minVertex.dist + e.w;
				predecessor[nextVertex.id] = minVertex.id;
				if(inqueue[nextVertex.id]  ==  true){
					queue.update(nextVertex);  //更新队列中的dist值
				}else{
					queue.add(nextVertex);
					inqueue[nextVertex.id] = true;
				}
			}
		}
	}
	//输出最短路径
	System.out.print(s);
	print(s , t, predecessor);
}

private void print (int s , int t ,int []  precessor){
	if( s == t) return ;
	print(s , predecessor[t] , predecessor);
	System.out.print("->" + t);
}

用Vertexes数组记录从起始顶点到每个顶点的距离dist,起初,把所有顶点的dist都初始化为无穷大,即代码中的Integer.MAX_VALUE,把起始顶点的dist值初始化为0,将其放到优先级队列中

从优先级队列中取出dist最小的顶点minVertex,然后考察这个顶点可达的所有顶点(nextVertex),如果minVertex的dist值加上minVertex与nextVertex之间边的权重w小于nextVertex当前的dist值,即存在另一条更短的路径,它经过minVertex到达nextVertex,把nextVertex的dist更新为minVertex的dist值加上w,重复过程,直到找到终止顶点t或队列为空

precessor数组是为了还原最短路径,它记录每个顶点的前驱顶点,最后通过递归,将路径打印出来

inqueue数组为了避免将一个顶点多次添加到优先级队列中,更新了某个顶点的dist值之后,如果这个顶点已经出现在优先级队列中,不再重复添加

如何计算最优出行路线?

只需要计算出一个可行的次优解就可以,虽然地图很大,但是两点之间的最短路径并不会很发散,所以我们可以在整个大地图上,画出一个小区块,小区块正好可以覆盖住两个点,但又不会很大,在这个小区块内运行Dijkstra算法

如何计算车通过一段路的时间?

每经过一条边,就要经过一个红绿灯,关于最少红绿灯的方案,只需要把每条边的权值都改为1,然后继续使用Dijkstra算法,还可以使用广度优先搜索算法,计算出来的两点之间的路径就是两点间的最短路径,真实的地图软件用的是类似A*的启发式搜索算法

总结引申

有一个翻译系统,只能针对单个词来做翻译,如果要翻译整个句子,需要将一个句子拆成一个一个单词,再给翻译系统,针对每个单词,系统返回一组可选的翻译列表,并且针对每个翻译打一个分,表示这个翻译的可信程度

句子: A B C 包含三个单词 A ,B C

A A:{a0,8}, {a1,6} , {a2,3}

B -> 翻译系统 -> B:{b0,6} , {b1 , 5} , {b2,4} , {b3,2}

C C:{C0,9} , {C1,6}

针对每个单词都可以从可选列表中,选择一个翻译组合起来就是整个句子的翻译,针对整个句子希望计算出来得分最高的前K个翻译结果,如何编程实现?

可以借助Dijkstra算法,每个单词的可选翻译是按照分数从大到小排列的,所以a0b0c0是得分最高组合结果,把这个作为一个对象放入到优先级队列中,每次从优先级队列中取出一个得分最高的组合,并给予这个组合进行拓展,扩展的策略是每个单词的翻译分别替换成下一个单词的翻译,比如a0b0c0扩展后,会得到三个组合a1b0c0 , a0b1c0 , a0b0c1 把扩展之后的组合都加到优先级队列中,重复这个过程,直到获取到K个翻译组合或者队列为空

在计算最短时间出行路线中,如何获取通过某条路的时间?

通过某条路的时间与 路长度 、路况、拥堵情况、 红绿灯个数等因素相关,获取该因素之后建立一个回归模型来估算时间

假设是开车出行,如果是公交出行呢?混合地铁公交步行呢?该如何规划路线?

地铁时刻表是固定的,可以估算,公交不准时,但大致时间是可以估计的,步行时间基本与道路长度成正比,更容易估算

发布了75 篇原创文章 · 获赞 9 · 访问量 9177

猜你喜欢

转载自blog.csdn.net/ywangjiyl/article/details/104717981