前言
最近由于换了工作,期间也有反思和总结上家公司的得失,总觉得有什么事情当初可以完成或者完成得更好,其中TSP问题就是其中之一。当初在开发一个仓配系统的时候,有一个线路排程的需求,当时自己简单在纸上画了思路,发现求精确解算法复杂度是N!,所以去百度,发现了NPC问题的概念,但是一直以来都没有对这个问题好好研究过,最终只是选择了贪心算法这一求近似解的方案,正好这是我的第一篇博客,就拿这个“遗憾”开刀吧。
1、 利用百度地图API模拟TSP的各个城市点
1.1、 调用百度地图API解析经纬度
这里首先到百度地图API官网申请一个apiKey,调地址解析接口会使用到,其中地址解析接口的参数可以访问http://lbsyun.baidu.com/index.php?title=uri/api/web开查看,这里我先将它截图:
将示例中的url(这里的output我采用json)在浏览器中输入,会得到如下结果:
这里利用HttpRequest方法来调用地址解析接口,类似于爬虫,最后将结果反序列化为实体类,代码如下:
///地址解析结果的实体类
///利用HttRequest调用API接口
1.2、利用经纬度计算两个地址间的距离
其实这个网上有大量雷同的计算方法,但是这个方法是我很久以前引用过来的,原出处已经翻不到了,只好在这里点出此算法非我原创,若有幸让原作者看到,请通知我注明出处。这里我先简单的介绍一下算法思路,以地球的球心作为坐标系原点作一个三维坐标系,如图:
其中坐标A(x1,y1,z1),坐标B(x2,y2,z2),注意一下,其中x=r*sin?*cos?,y=r* r*sin?*sin?,z=r*cos ?,这里将角度转化为π来计算,让经纬度*π/180,那么坐标点在北纬时:?=π/2-纬度*π/180;坐标点在南纬时:?=π/2+纬度*π/180;坐标点在西经时:?=π*2-经度*π/180;这个换算完成后,就可以通过给(x1-x2)^2+(y1-y2)^2+(z1-z2)^2开方计算出两点间的直线距离d,然后通过余弦定理求两点之间的夹角,最后计算出两点之间的球面距离,代码如下:
2、 通过群蚁算法求最短路径的近似解
关于群蚁算法,网上的介绍也有很多,不过偷下懒,只关注了C#的实现,这里参考的是园子里数据之巅的群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法 ,我就指出我对这篇文章理解起来比较费力的几点吧。
(1)这里的轮盘赌注法是遗传算法的一种思路,即通过产生一个随机数来选择下一个访问的城市,可以把0.081、0.74、0.18视为[0,0.081],[0.081,0.821],[0.821,1]这么三个区间,然后产生一个0到1的随机数(保留两位有效小数),当这个随机小数属于区间[0,0.081]时,选择城市B,属于[0.081,0.821]时,选择城市C,属于[0.821,1]时,选择城市D。这里是对群蚁算法的一种优化,优化方式有多种,可以自行查阅资料。
(2)代码处关于List的深复制
这个原作者没有给出实现,我用拓展方法实现了一下,代码如下:
(3)贪心算法的实现
这个原作者也没有给出,于是我自己实现了一下(可能在时间和空间复杂调用上有很大缺陷,这个的优化工作本人暂不处理,大家有兴趣的可以自己实现),代码如下:
整个群蚁算法的代码基本沿用了原作者的代码,没有什么改变,所以就不贴整套的了。
3、 测试代码:
运行结果:
这里注意调整BaseTspAntSystem类构造方法的参数,信息素越强(参数a),即能见度越高,收敛性越强,得到的结果路径就越单一,精确性会差一些,大家可以自己调整下参数观察不同的结果。
4、 总结
这里基本上是在收集别人的代码和成果来解决自己的问题,文章引用的大部分也都是别人的代码,但重点是,一定要自己一点点的理解和尝试。
参考资料:
【1】新浪博客,吴超,根据两点经纬度计算距离【转】;
【2】博客园,数据之巅,群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法;
【3】网易博客,蚁群优化算法ACO ;
【4】博客园,柠檬雨,Evolutionary Computing: 遗传算法_轮盘赌选择(转载);
【5】博客园,Learning hard, [C#进阶系列]专题一:深入解析深拷贝和浅拷贝;