码农家园

关闭
导航

关于性能:如何在大空间尺度上加速A *算法?


a-staralgorithmnetlogoperformance

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

不幸的是,我不知道这个代码的速度。有一个更新的解决方案,在最小成本路径的大范围的空间尺度?

太感谢你的帮助。

相关讨论

  • 我一点也不懂这种语言,但是你每次迭代都会用到sort-by和list-open,我不知道这是否会增加开销。另外,G的设置可能是"当前路径的成本+1",这可能会给你一个巨大的提升。我对这门语言的了解还不足以提出进一步的建议,但它有几个特点,在速度较慢的语言中很常见。
  • 我觉得这个密码不错。如果其中有效率方面的错误,我不会发现。也许你正在尝试做的计算量太大了,你需要找出一种方法来解决你的问题,以减少计算量。使用不一定找到最佳路径,但倾向于选择合理好路径的启发式方法怎么样?


我很好奇所以我测试了我的A*这是我的结果

迷宫1280 x 800 x 32位像素

A* test

  • 如你所见,花了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;
        }
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

我知道代码有点太多,但它是完整的。重要的是成员函数compute,所以搜索[A* changed points list]。未优化的EDOCX1(rem ed)大约慢100倍。

代码使用BorlandVCL中的位图,因此如果没有位图,则忽略函数get,set,并将其重写为输入/输出gfx样式。他们只是从bitmap加载map并将计算出的map绘制回bitmap。

用途:

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
相关讨论

  • 如果将路径从起点和终点强制转换到路径连接,则速度可以加快<=2倍。但是它需要更大的点列表和稍微不同的路径编码…加速取决于地图和增加的开销(有时甚至会降低速度)。
  • 非常感谢你的演讲。由于我从未使用过C++语言,所以我不知道如何编译和执行代码。我用编译器dev-c++尝试过,但得到了几个消息(例如,dword没有命名一个类型)。事先谢谢你的帮助。
  • @marine dword是32位无符号int,可以使用任何整数类型,但如果使用有符号数字,则必须从0xfffffff更改u星u空间和u星u墙常量?到0x7FFFFFF?…在您的例子中,我将在源代码的开头添加typedef unsigned int dword;。此外,您还必须重写get,将函数设置为您的环境(我使用BorlandVCL中的位图,所以只需将其更改为您使用的数据类型…)。你也可以试着从C++到你的语言(我完全不认识它)。
  • 这个答案与这个问题有什么关系?这有什么意义?
  • @moooeeep这是一个比较的例子,可以排除分辨率太大而无法快速计算。由于OP缺少关于HW和运行时间的信息,因此可以看出,这在相对较短的时间内是圆顶。所以速度问题是其他原因造成的。


A*是两种启发式算法;Djikstra的算法和贪婪搜索。Djikstra的算法搜索最短路径。贪婪的搜索寻找最便宜的路径。Djikstra的算法非常慢,因为它不冒风险。增加贪婪搜索的效果以承担更多的风险。

例如,如果A* = Djikstra + Greedy,则为更快的A* = Djikstra + 1.1 * Greedy。无论您如何优化内存访问或代码,它都不会修复解决问题的错误方法。让你的A*更贪婪,它会专注于找到一个解决方案,而不是一个完美的解决方案。

注:

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*模型(相当低效)的图片中可以看到,一种幼稚的方法会产生很多额外的步骤。在长直走廊中创建的任何开放节点都将被浪费:

Sample maze solved with naive a-star

通过从图中删除这些步骤,可以更快地找到解决方案。

另一种稀疏图技术是使用一个距离步行者越远密度越小的图。也就是说,让你的搜索空间在助行器附近更详细,并且远离助行器稀疏(节点更少,障碍物更不准确)。当步行者在地图上的详细地形中移动时,这尤其有用,因为地图正在改变或朝向移动的目标移动,而且无论如何都必须重新计算路线。

例如,在交通模拟中,道路可能堵塞或发生事故。同样,一种模拟,其中一个代理在不断变化的环境中追求另一个代理。在这些情况下,只需要精确地绘制接下来的几个步骤。到目的地的一般路线可以是近似的。

实现这一点的一个简单方法是,随着路径的变长,逐步增加步行者的步进大小。忽略障碍物或进行快速直线交叉或切线测试。这使步行者对去哪里有了大致的了解。

改进后的路径可以用每一步重新计算,或者定期重新计算,或者在遇到障碍时重新计算。

它可能只节省了几毫秒,但是在即将改变的路径末端浪费的几毫秒可以更好地用于为更多的步行者提供大脑,或者更好的图形,或者更多的时间与家人在一起。

对于一个不同密度的稀疏图的例子,参见David Wallace Croft的高级Java编程的第8章:Apress:HTTP//www. ApRES.COM/GAME-PLICANG/JAVA/981590591239

他在一个演示坦克游戏中使用了一个增加稀疏度的圆形图,并用A*算法驱动敌人的坦克。

另一种稀疏图方法是用唯一的兴趣点填充图。例如,要绘制一条穿过简单建筑校园的路线,只有入口、出口和拐角是很重要的。沿着建筑物侧面或在建筑之间的空地上的点并不重要,可以从搜索图中省略。更详细的地图可能需要更多的路径点——比如喷泉或雕像周围的一圈节点,或者铺好的路径相交的地方。

这是一个显示路径点之间路径的图表。

Sample of building corner waypoints for path-search optimization

这是由我在Turtlezero.com上创建的校园建筑路径图模型生成的:http://www.turtlezero.com/models/view.php?模型=校园建筑路径图

它使用简单的netlogo补丁查询来查找感兴趣的点,比如外角和内角。我相信一组更复杂的查询可以处理像对角墙之类的问题。但是,即使没有这种复杂的进一步优化,A*搜索空间也将减少数量级。

不幸的是,由于Java 1.7不允许未签名的小程序,所以不能在网页中运行模型,而不必调整Java安全设置。很抱歉。但是读一下描述。


如果计划多次重复使用同一个映射,则某种形式的预处理通常是最佳的。实际上,您计算出一些公共点之间的最短距离,并将它们作为边添加到图中,这通常有助于*更快地找到解决方案。虽然更难实施。

例如,您可以在英国地图中为所有高速公路路线执行此操作,因此搜索算法只需找到通往高速公路的路线,以及从高速公路交叉口到目的地的路线。


我不知道观察到的懒散的真正原因是什么。也许这仅仅是由于手边的编程语言在效率上的缺陷。你是如何衡量你的表现的?我们怎样才能复制它?

此外,所使用的启发式(距离度量)对为找到最佳路径而进行的探索量有很大影响,从而也影响了算法的感知效率。

理论上,你必须使用一种可接受的启发式方法,即从不高估剩余距离的方法。实际上,根据迷宫的复杂程度,对于二维网格迷宫(如曼哈顿距离)的保守选择可能会大大低估剩余距离。因此,在远离目标的迷宫区域进行了大量的探索。这导致了一种程度上的探索,这种探索与详尽的搜索(例如,广度优先搜索)非常相似,而不是人们对知情搜索算法的期望。

这可能是需要调查的问题。

还可以看看我的相关答案:

  • https://stackoverflow.com/a/16656993/1025391

在这里,我将不同的启发式方法与基本的A星算法进行了比较,并将结果可视化。你可能会觉得有趣。


  • 关于python:表示和求解给定图像的迷宫
  • 关于SQL:数据库索引如何工作?
  • JavaScript如何检查数组是否包含对象?
  • 关于.NET:重写System.Object.GetHashCode的最佳算法是什么?
  • 关于性能:如何加速Eclipse?
  • 关于性能:为什么Android模拟器这么慢?我们如何加速Android模拟器?
  • 关于字符串:纯英语中的Ukkonen后缀树算法
  • 关于C++:图像处理:"可口可乐罐头"识别算法的改进

Copyright ©  码农家园 联系:[email protected]