GIS系统中最短路径查找算法优化之一:双向广度优先搜索
2010-10-20 21:33
761 查看
去年毕业的时候进入一家GIS软件开发公司,正式接手的第一个项目是GIS系统的拓扑分析,在公司原有的算法基础上进行维护和优化。在优化过程有了以下心得,本该去年就更新在blog上的,但是一直没有时间去弄,现更新之。
在GIS系统中,最常用的一个功能就是在拓扑网络结构中进行最短路径分析。
注:最短路径分析公司取名为FindAPath.
公司前辈最初使用的算法是广度优先算法。因为广搜一旦搜索到解即为最小的步数,这是深搜所忘尘莫及的。
代码流程大致如下:
以上代码大致是我凭记忆写下来的,之前的代码备份地址暂时没找到。优化过程中我选择使用双向广度优先搜索算法。
同样的逻辑不同的是同时从起点和终点向中间搜索遇到交叉点中止。论坛很多人认为双广其实没有什么提高,这样看业务层导致数据层的依赖关系,在我的GIS项目中,一个点设备大部分都有4个子节点,那么广搜层数越大,子节点数成字数增长。在我的业务系统中,双搜层数总会比单搜层数少。比如单搜需要4层找到,那么经过的节点数会在4的4次方即256个,双搜一般会两个层中止即2的4次方乘以2为32.后面将列出我的测试数据。但是极端情况下双搜可能适得其反,例如起点和终点完全没有连通性的时候,搜索个数会加倍。
以下是我的代码:
以下是优化的测试报告。
在GIS系统中,最常用的一个功能就是在拓扑网络结构中进行最短路径分析。
注:最短路径分析公司取名为FindAPath.
公司前辈最初使用的算法是广度优先算法。因为广搜一旦搜索到解即为最小的步数,这是深搜所忘尘莫及的。
代码流程大致如下:
list<long> MiddleNodes; //用于保存待搜索子节点 list<long>::iterator listIter; map<long,long> PreMap; //用于保存搜索路径 map<long,long>::iterator mapIter; set<long> SearchNodes; //已搜索过的节点eid,防止环路,死循环 set<long>::iterator setIter; MiddleNodes.push_back(lBeginNode);//初始化分析起点 PreMap[lBeginNode] = -1;//用于在回溯路径的时候中止条件 while(!MiddleNodes.empty()) { listIter = MiddleNodes.begin(); MiddleNodes.erase(listIter); //取要搜索的,在队列中删除之 SearchNodes.insert(*listIter); //将搜索过的节点记录下来 //获取到待分析点的所有子节点,函数设计业务不列出 list<long> lChildNods = GetChildNodes(*ListIter); list<long>::iterator tempIter(lChildNods.begin()); list<long>::iterator EndIter(lChildNods.end()); while (tempIter != EndIter) { setIter = SearchNodes.find(*tempIter); //防止回环 if (setIter == SearchNodes.end()) { PreMap[*setIter] = *tempIter;//记录每条路径 MiddleNodes.push_back(*setIter);//将子节点插入到待分析队列 } ++tempIter; } } list<long> ListResult; //保存结果路径 long lTempValue = -1; //遍历路径 while (1) { mapIter = PreMap.find(lEndNode); lTempValue = (*mapIter).second; if(lTempValue==-1) break; else { ListResult.push_front(lTempValue); } }
以上代码大致是我凭记忆写下来的,之前的代码备份地址暂时没找到。优化过程中我选择使用双向广度优先搜索算法。
同样的逻辑不同的是同时从起点和终点向中间搜索遇到交叉点中止。论坛很多人认为双广其实没有什么提高,这样看业务层导致数据层的依赖关系,在我的GIS项目中,一个点设备大部分都有4个子节点,那么广搜层数越大,子节点数成字数增长。在我的业务系统中,双搜层数总会比单搜层数少。比如单搜需要4层找到,那么经过的节点数会在4的4次方即256个,双搜一般会两个层中止即2的4次方乘以2为32.后面将列出我的测试数据。但是极端情况下双搜可能适得其反,例如起点和终点完全没有连通性的时候,搜索个数会加倍。
以下是我的代码:
long NodeNo = 0; long LineNo = 0; long middlePoint = 0; long middleLine = 0; long DataCnt = 0; bool bForceBreak = false; //若找到终止点,强制退出while循环 long valOfBegin = 0, parentOfBegin = 0; long valOfEnd = 0, parentOfEnd = 0; vector<long> pData; list<long> MiddleNodesOfBegin; list<long>::iterator listIterOfBegin; map<long,long> PreMapOfBegin; map<long,long>::iterator mapIterOfBegin; list<long> MiddleNodesOfEnd; list<long>::iterator listIterOfEnd; map<long,long> PreMapOfEnd; map<long,long>::iterator mapIterOfEnd; //如果起点和终点为同一个点。 if(BeginEid == EndEid) { Nodes.push_back (BeginEid); return 1; } //1222--modify by buffer--使用同时从起点和终点搜索路径的算法 MiddleNodesOfBegin.push_back(BeginEid); MiddleNodesOfEnd.push_back(EndEid); PreMapOfBegin[BeginEid] = -2; PreMapOfEnd[EndEid] = -2; while((!MiddleNodesOfBegin.empty()) && (!MiddleNodesOfEnd.empty())) { //从前端查找一次 valOfBegin = MiddleNodesOfBegin.front(); MiddleNodesOfBegin.pop_front(); mapIterOfBegin = PreMapOfBegin.find(valOfBegin); parentOfBegin = (*mapIterOfBegin).second; GetAdjacentDataByEid(valOfBegin, versionNo, pData); DataCnt = pData.size()/2; if(DataCnt <= 0) { continue; } for(long i=0; i<DataCnt; i++) { long NodeValue = 0, LineValue = 0; NodeValue = pData[2*i+1]; LineValue = pData[2*i]; if (NodeValue!=-1 && NodeValue!=valOfBegin && NodeValue!=parentOfBegin) { mapIterOfBegin = PreMapOfBegin.find(NodeValue); if(mapIterOfBegin == PreMapOfBegin.end()) { MiddleNodesOfBegin.push_back(NodeValue); PreMapOfBegin[LineValue] = valOfBegin; PreMapOfBegin[NodeValue] = LineValue; } //发现有交点,则找到连通路径结束 mapIterOfEnd = PreMapOfEnd.find(NodeValue); if(mapIterOfEnd != PreMapOfEnd.end()) { middlePoint = NodeValue; bForceBreak = true; break; } } } if(bForceBreak) { break; } //从后端搜索一次 valOfEnd = MiddleNodesOfEnd.front(); MiddleNodesOfEnd.pop_front(); mapIterOfEnd = PreMapOfEnd.find(valOfEnd); parentOfEnd = (*mapIterOfEnd).second; GetAdjacentDataByEid(valOfEnd, versionNo, pData); DataCnt = pData.size()/2; if(DataCnt <= 0) { continue; } for(long i=0; i<DataCnt; i++) { long NodeValue = 0, LineValue = 0; NodeValue = pData[2*i+1]; LineValue = pData[2*i]; if (NodeValue!=-1 && NodeValue!=valOfEnd && NodeValue!=parentOfEnd) { mapIterOfEnd = PreMapOfEnd.find(NodeValue); if(mapIterOfEnd == PreMapOfEnd.end()) { MiddleNodesOfEnd.push_back(NodeValue); PreMapOfEnd[LineValue] = valOfEnd; PreMapOfEnd[NodeValue] = LineValue; } //发现有交点,则找到连通路径结束 mapIterOfBegin = PreMapOfBegin.find(NodeValue); if(mapIterOfBegin != PreMapOfBegin.end()) { middlePoint = NodeValue; bForceBreak = true; break; } } } if(bForceBreak) { break; } } //logger->debug("SearchNodes from the begin--->%ld", PreMapOfBegin.size()/2); //logger->debug("SearchNodes from the end--->%ld", PreMapOfEnd.size()/2); if(!bForceBreak) { return -1; //两点间不连通 } //得出起点开始的路径 long tmpPoint = middlePoint; while(-1 != middlePoint) { Nodes.push_front(middlePoint); NodeNo++; mapIterOfBegin = PreMapOfBegin.find(middlePoint); middleLine = (*mapIterOfBegin).second; if (-2 == middleLine) { break; } Lines.push_front(middleLine); LineNo++; mapIterOfBegin = PreMapOfBegin.find(middleLine); middlePoint = (*mapIterOfBegin).second; } //将终点搜索到的路径和从起点搜索到的路径合并 mapIterOfEnd = PreMapOfEnd.find(tmpPoint); middleLine = (*mapIterOfEnd).second; while (-2 != middleLine) { Lines.push_back(middleLine); LineNo++; mapIterOfEnd = PreMapOfEnd.find(middleLine); middlePoint = (*mapIterOfEnd).second; Nodes.push_back(middlePoint); NodeNo++; mapIterOfEnd = PreMapOfEnd.find(middlePoint); middleLine = (*mapIterOfEnd).second; }
以下是优化的测试报告。
修改算法从两个断点开始同时向中间搜索经过测试的结果是: 1.在两点之间有联通关系的情况下 输入参数为:FindAPath;V_T_PD_SB_GSGLKG_GEO;54;V_T_PD_SB_DL_GEO;2000;0 修改算法前的效率为: 时间为: 13359.00ms 中间走过的节点为: 81070个点 修改算法后的效率为: 时间为: 844.00ms 中间走过的节点为: 2037+1935个点 2.在两点之间没有联通关系的情况下 输入参数为:FINDAPATH;V_T_PD_SB_GSGLKG_GEO;482;V_T_PD_SB_DLZDT_GEO;1424;0 修改算法前的效率为: 时间为: 47000.00ms 中间走过的节点为: 284840个点 修改算法后的效率为: 时间为: 73015.00ms 中间走过的节点为: 284614+288803个点
相关文章推荐
- 地铁线路图高性能查找算法系统,最短路径查询地铁网络拓扑高效率算法-原创-附带demo
- HDOJ 3790 最短路径问题 (dijkstra算法的优化,优先队列)
- 广度优先搜索求最短路径问题
- 最短路径 A*算法 应用堆优化
- HDU-3790 最短路径问题(两个权值,Dijkstra,(含堆优化))
- 最短路径之迪杰斯特拉与双向迪杰斯特拉实验结果
- 城市最短路径问题--图的广度优先搜索
- 最短路径——优先队列优化版(其实稠密图不优化)
- 单点最短路径算法 bellman-ford模板和队列优化后的spfa算法模板
- 利用Matlab优化工具箱求解旅行商最短路径问题
- 最短路径之 Dijkstra的优化
- 求解最短路径Bellman_Ford 算法优化版——结合队列
- 无权最短路径 广度优先搜索
- PAT 1030 Travel Plan(单源最短路径+优化Dijkstra)
- Dijkstra 最短路径 优化
- 无权最短路径-广度优先搜索
- P3371 【模板】单源最短路径 SPFA优化 dijkstra堆优化
- 南沙政府应急系统之GIS一张图(arcgis api for flex)讲解(十一)路径导航模块
- 总结一下最短路径的贝尔曼-福特算法(Bellman-Ford)及用队列优化(spfa)
- 洛谷P3371 单源最短路径(Dijkstra+堆优化)