您的位置:首页 > 其它

GIS系统中最短路径查找算法优化之一:双向广度优先搜索

2010-10-20 21:33 761 查看
去年毕业的时候进入一家GIS软件开发公司,正式接手的第一个项目是GIS系统的拓扑分析,在公司原有的算法基础上进行维护和优化。在优化过程有了以下心得,本该去年就更新在blog上的,但是一直没有时间去弄,现更新之。

在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个点
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: