How to speed up A* algorithm at large spatial scales?
从http:/ / / / ccl.northwestern.edu NetLogo模特/社区/ astardemo I编码的A *算法,通过使用一个网络节点的最小成本路径的定义。的代码似乎工作,但它是太慢时,我会使用它在大的空间尺度上的景观。我院有1000 x 1000个补丁补丁补丁的像素1 = 1。即使我减少它在400×400的补丁的补丁的补丁在1 = 1的像素,但它是太慢(i can’t修改下面的补丁的补丁景观400×400)。这里是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | to find-path [ source-node destination-node] let search-done? false let search-path [] let current-node 0 set list-open [] set list-closed [] let list-links-with-nodes-in-list-closed [] let list-links [] set list-open lput source-node list-open while [ search-done? != true] [ ifelse length list-open != 0 [ set list-open sort-by [[f] of ?1 < [f] of ?2] list-open set current-node item 0 list-open set list-open remove-item 0 list-open set list-closed lput current-node list-closed ask current-node [ if parent-node != 0[ set list-links-with-nodes-in-list-closed lput link-with parent-node list-links-with-nodes-in-list-closed ] ifelse any? (nodes-on neighbors4) with [ (xcor = [ xcor ] of destination-node) and (ycor = [ycor] of destination-node)] [ set search-done? true ] [ ask (nodes-on neighbors4) with [ (not member? self list-closed) and (self != parent-node) ] [ if not member? self list-open and self != source-node and self != destination-node [ set list-open lput self list-open set parent-node current-node set list-links sentence (list-links-with-nodes-in-list-closed) (link-with parent-node) set g sum (map [ [link-cost] of ? ] list-links) set h distance destination-node set f (g + h) ] ] ] ] ] [ user-message("A path from the source to the destination does not exist." ) report [] ] ] set search-path lput current-node search-path let temp first search-path while [ temp != source-node ] [ ask temp [ set color red ] set search-path lput [parent-node] of temp search-path set temp [parent-node] of temp ] set search-path fput destination-node search-path set search-path reverse search-path print search-path end |
不幸的是,我不知道这个代码的速度。有一个更新的解决方案,在最小成本路径的大范围的空间尺度?
太感谢你的帮助。
我很好奇所以我测试了我的A*这是我的结果
迷宫1280 x 800 x 32位像素
- 如你所见,花了23毫秒
- 无多线程(AMD 3.2GHz)
- C++ 32位应用程序(BDS2006 Turbo C++或Borland C++ Builder 2006,如果您愿意)
- 我找到的最慢的路径大约是44毫秒(几乎填满整个地图)
我觉得这足够快了…
以下是我的A*类源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- const DWORD A_star_space=0xFFFFFFFF; const DWORD A_star_wall =0xFFFFFFFE; //--------------------------------------------------------------------------- class A_star { public: // variables DWORD **map; // map[ys][xs] int xs,ys; // map esolution xs*ys<0xFFFFFFFE !!! int *px,*py,ps; // output points px[ps],py[ps] after compute() // internals A_star(); ~A_star(); void _freemap(); // release map memory void _freepnt(); // release px,py memory // inteface void resize(int _xs,int _ys); // realloc map to new resolution void set(Graphics::TBitmap *bmp,DWORD col_wall); // copy bitmap to map void get(Graphics::TBitmap *bmp); // draw map to bitmap for debuging void compute(int x0,int y0,int x1,int y1); // compute path from x0,y0 to x1,y1 output to px,py }; //--------------------------------------------------------------------------- A_star::A_star() { map=NULL; xs=0; ys=0; px=NULL; py=NULL; ps=0; } A_star::~A_star() { _freemap(); _freepnt(); } void A_star::_freemap() { if (map) delete[] map; map=NULL; xs=0; ys=0; } void A_star::_freepnt() { if (px) delete[] px; px=NULL; if (py) delete[] py; py=NULL; ps=0; } //--------------------------------------------------------------------------- void A_star::resize(int _xs,int _ys) { if ((xs==_xs)&&(ys==_ys)) return; _freemap(); xs=_xs; ys=_ys; map=new DWORD*[ys]; for (int y=0;y<ys;y++) map[y]=new DWORD[xs]; } //--------------------------------------------------------------------------- void A_star::set(Graphics::TBitmap *bmp,DWORD col_wall) { int x,y; DWORD *p,c; resize(bmp->Width,bmp->Height); for (y=0;y<ys;y++) for (p=(DWORD*)bmp->ScanLine[y],x=0;x<xs;x++) { c=A_star_space; if (p[x]==col_wall) c=A_star_wall; map[y][x]=c; } } //--------------------------------------------------------------------------- void A_star::get(Graphics::TBitmap *bmp) { int x,y; DWORD *p,c; bmp->SetSize(xs,ys); for (y=0;y<ys;y++) for (p=(DWORD*)bmp->ScanLine[y],x=0;x<xs;x++) { c=map[y][x]; if (c==A_star_wall ) c=0x00000000; else if (c==A_star_space) c=0x00FFFFFF; else c=((c>>1)&0x7F)+0x00404040; p[x]=c; } } //--------------------------------------------------------------------------- void A_star::compute(int x0,int y0,int x1,int y1) { int x,y,xmin,xmax,ymin,ymax,xx,yy; DWORD i,j,e; // [clear previous paths] for (y=0;y<ys;y++) for (x=0;x<xs;x++) if (map[y][x]!=A_star_wall) map[y][x]=A_star_space; /* // [A* no-optimizatims] xmin=x0; xmax=x0; ymin=y0; ymax=y0; if (map[y0][x0]==A_star_space) for (i=0,j=1,e=1,map[y0][x0]=i;(e)&&(map[y1][x1]==A_star_space);i++,j++) for (e=0,y=ymin;y<=ymax;y++) for ( x=xmin;x<=xmax;x++) if (map[y][x]==i) { yy=y-1; xx=x; if ((yy>=0)&&(map[yy][xx]==A_star_space)){ map[yy][xx]=j; e=1; if (ymin>yy) ymin=yy; } yy=y+1; xx=x; if ((yy<ys)&&(map[yy][xx]==A_star_space)){ map[yy][xx]=j; e=1; if (ymax<yy) ymax=yy; } yy=y; xx=x-1; if ((xx>=0)&&(map[yy][xx]==A_star_space)){ map[yy][xx]=j; e=1; if (xmin>xx) xmin=xx; } yy=y; xx=x+1; if ((xx<xs)&&(map[yy][xx]==A_star_space)){ map[yy][xx]=j; e=1; if (xmax<xx) xmax=xx; } } */ // [A* changed points list] // init space for 2 points list _freepnt(); int i0=0,i1=xs*ys,n0=0,n1=0,ii; px=new int[i1*2]; py=new int[i1*2]; // if start is not on space then stop if (map[y0][x0]==A_star_space) { // init start position to first point list px[i0+n0]=x0; py[i0+n0]=y0; n0++; map[y0][x0]=0; // search until hit the destination (swap point lists after each iteration and clear the second one) for (j=1,e=1;(e)&&(map[y1][x1]==A_star_space);j++,ii=i0,i0=i1,i1=ii,n0=n1,n1=0) // test neibours of all points in first list and add valid new points to second one for (e=0,ii=i0;ii<i0+n0;ii++) { x=px[ii]; y=py[ii]; yy=y-1; xx=x; if ((yy>=0)&&(map[yy][xx]==A_star_space)){ map[yy][xx]=j; e=1; px[i1+n1]=xx; py[i1+n1]=yy; n1++; map[yy][xx]=j; } yy=y+1; xx=x; if ((yy<ys)&&(map[yy][xx]==A_star_space)){ map[yy][xx]=j; e=1; px[i1+n1]=xx; py[i1+n1]=yy; n1++; map[yy][xx]=j; } yy=y; xx=x-1; if ((xx>=0)&&(map[yy][xx]==A_star_space)){ map[yy][xx]=j; e=1; px[i1+n1]=xx; py[i1+n1]=yy; n1++; map[yy][xx]=j; } yy=y; xx=x+1; if ((xx<xs)&&(map[yy][xx]==A_star_space)){ map[yy][xx]=j; e=1; px[i1+n1]=xx; py[i1+n1]=yy; n1++; map[yy][xx]=j; } } } // [reconstruct path] _freepnt(); if (map[y1][x1]==A_star_space) return; if (map[y1][x1]==A_star_wall) return; ps=map[y1][x1]+1; px=new int[ps]; py=new int[ps]; for (i=0;i<ps;i++) { px[i]=x0; py[i]=y0; } for (x=x1,y=y1,i=ps-1,j=i-1;i>=0;i--,j--) { px[i]=x; py[i]=y; if ((y> 0)&&(map[y-1][x]==j)) { y--; continue; } if ((y<ys-1)&&(map[y+1][x]==j)) { y++; continue; } if ((x> 1)&&(map[y][x-1]==j)) { x--; continue; } if ((x<xs-0)&&(map[y][x+1]==j)) { x++; continue; } break; } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- |
我知道代码有点太多,但它是完整的。重要的是成员函数
代码使用BorlandVCL中的位图,因此如果没有位图,则忽略函数
用途:
1 2 3 4 5 6 7 8 9 10 | // init A_star map; Graphics::TBitmap *maze=new Graphics::TBitmap; maze->LoadFromFile("maze.bmp"); maze->HandleType=bmDIB; maze->PixelFormat=pf32bit; map.set(maze,0); // walls are 0x00000000 (black) // this can be called repetitive without another init map.compute(x0,y0,x1,y1); // map.px[map.ps],map.py[map.ps] holds the path map.get(maze,0); // this is just for drawing the result map back to bitmap for viewing |
A*是两种启发式算法;Djikstra的算法和贪婪搜索。Djikstra的算法搜索最短路径。贪婪的搜索寻找最便宜的路径。Djikstra的算法非常慢,因为它不冒风险。增加贪婪搜索的效果以承担更多的风险。
例如,如果
注:
1 2 | Greedy Search = distance from end Djikstra's Algorithm = distance from start |
在标准A*中,它将寻求完美的解决方案,直到遇到障碍。这段视频显示了不同的搜索启发式算法;注意贪婪搜索的速度有多快(对于一个*,跳到2:22,对于贪婪,跳到4:40)。我自己也遇到了类似的问题,当我第一次使用a*开始时,上面修改的a*i提纲以指数形式提高了我的性能。故事的寓意;为工作使用正确的工具。
tl;dr:在节点列表(图形)中只包括重要的补丁(或代理)!
加快速度的一个方法是不要搜索每个网格空间。A*是一种图形搜索,但似乎大多数编码人员只是将网格中的每个点都转储到图形中。这不是必需的。使用稀疏的搜索图,而不是搜索屏幕上的每个点,可以加快速度。
即使在一个复杂的迷宫中,你也可以通过在图中只包含角点和交叉点来加速。不要将走廊网格添加到开放列表中——向前搜索以找到下一个拐角或交叉点。这就是预处理屏幕/网格/地图以构建搜索图的地方,可以在以后节省时间。
从我在TurtleZero.com上的A*模型(相当低效)的图片中可以看到,一种幼稚的方法会产生很多额外的步骤。在长直走廊中创建的任何开放节点都将被浪费:
通过从图中删除这些步骤,可以更快地找到解决方案。
另一种稀疏图技术是使用一个距离步行者越远密度越小的图。也就是说,让你的搜索空间在助行器附近更详细,并且远离助行器稀疏(节点更少,障碍物更不准确)。当步行者在地图上的详细地形中移动时,这尤其有用,因为地图正在改变或朝向移动的目标移动,而且无论如何都必须重新计算路线。
例如,在交通模拟中,道路可能堵塞或发生事故。同样,一种模拟,其中一个代理在不断变化的环境中追求另一个代理。在这些情况下,只需要精确地绘制接下来的几个步骤。到目的地的一般路线可以是近似的。
实现这一点的一个简单方法是,随着路径的变长,逐步增加步行者的步进大小。忽略障碍物或进行快速直线交叉或切线测试。这使步行者对去哪里有了大致的了解。
改进后的路径可以用每一步重新计算,或者定期重新计算,或者在遇到障碍时重新计算。
它可能只节省了几毫秒,但是在即将改变的路径末端浪费的几毫秒可以更好地用于为更多的步行者提供大脑,或者更好的图形,或者更多的时间与家人在一起。
对于一个不同密度的稀疏图的例子,参见David Wallace Croft的高级Java编程的第8章:Apress:HTTP//www. ApRES.COM/GAME-PLICANG/JAVA/981590591239
他在一个演示坦克游戏中使用了一个增加稀疏度的圆形图,并用A*算法驱动敌人的坦克。
另一种稀疏图方法是用唯一的兴趣点填充图。例如,要绘制一条穿过简单建筑校园的路线,只有入口、出口和拐角是很重要的。沿着建筑物侧面或在建筑之间的空地上的点并不重要,可以从搜索图中省略。更详细的地图可能需要更多的路径点——比如喷泉或雕像周围的一圈节点,或者铺好的路径相交的地方。
这是一个显示路径点之间路径的图表。
这是由我在Turtlezero.com上创建的校园建筑路径图模型生成的:http://www.turtlezero.com/models/view.php?模型=校园建筑路径图
它使用简单的netlogo补丁查询来查找感兴趣的点,比如外角和内角。我相信一组更复杂的查询可以处理像对角墙之类的问题。但是,即使没有这种复杂的进一步优化,A*搜索空间也将减少数量级。
不幸的是,由于Java 1.7不允许未签名的小程序,所以不能在网页中运行模型,而不必调整Java安全设置。很抱歉。但是读一下描述。
如果计划多次重复使用同一个映射,则某种形式的预处理通常是最佳的。实际上,您计算出一些公共点之间的最短距离,并将它们作为边添加到图中,这通常有助于*更快地找到解决方案。虽然更难实施。
例如,您可以在英国地图中为所有高速公路路线执行此操作,因此搜索算法只需找到通往高速公路的路线,以及从高速公路交叉口到目的地的路线。
我不知道观察到的懒散的真正原因是什么。也许这仅仅是由于手边的编程语言在效率上的缺陷。你是如何衡量你的表现的?我们怎样才能复制它?
此外,所使用的启发式(距离度量)对为找到最佳路径而进行的探索量有很大影响,从而也影响了算法的感知效率。
理论上,你必须使用一种可接受的启发式方法,即从不高估剩余距离的方法。实际上,根据迷宫的复杂程度,对于二维网格迷宫(如曼哈顿距离)的保守选择可能会大大低估剩余距离。因此,在远离目标的迷宫区域进行了大量的探索。这导致了一种程度上的探索,这种探索与详尽的搜索(例如,广度优先搜索)非常相似,而不是人们对知情搜索算法的期望。
这可能是需要调查的问题。
还可以看看我的相关答案:
- https://stackoverflow.com/a/16656993/1025391
在这里,我将不同的启发式方法与基本的A星算法进行了比较,并将结果可视化。你可能会觉得有趣。