设为首页 收藏本站
查看: 854|回复: 0

[经验分享] Perl寻路A*算法实现

[复制链接]

尚未签到

发表于 2015-12-26 12:59:12 | 显示全部楼层 |阅读模式
  A*算法;A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法。估价值与实际值越接近,估价函数取得就越好。
  公式表示为: f(n)=g(n)+h(n),其中 f(n) 是从初始点经由节点n到目标点的估价函数,g(n) 是在状态空间中从初始节点到n节点的实际代价,h(n) 是从n到目标节点最佳路径的估计代价。
  保证找到最短路径(最优解的)条件,关键在于估价函数f(n)的选取:估价值h(n)<= n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。并且如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。如果 估价值>实际值,搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
    创建两个表,OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。算起点的估价值;将起点放入OPEN表;保存路径,即从终点开始,每个节点沿着父节点移动直至起点,这就是你的路径。
  算法伪码:



while(OPEN!=NULL)
{
从OPEN表中取估价值f(n)最小的节点n;
if(n节点==目标节点)
break;
for(当前节点n的每个子节点X)
{
算X的估价值;
if(XinOPEN)
if(X的估价值小于OPEN表的估价值)
{
把n设置为X的父亲;
更新OPEN表中的估价值;//取最小路径的估价值
            }
if(XinCLOSE)
continue;
if(Xnotinboth)
{
把n设置为X的父亲;
求X的估价值;
并将X插入OPEN表中;//还没有排序
        }
}//endfor
    将n节点插入CLOSE表中;
按照估价值将OPEN表中的节点排序;//实际上是比较OPEN表内节点f的大小,从最小路径的节点向下进行。
}//endwhile(OPEN!=NULL)
  以上摘自百度百科,以下将用Perl语言实现:  
  1、建立路径数组,下标即是步数,并使用匿名哈希保存坐标点、开销、到目的地开销、实际开销、父节点等信息,数据结构如下:
  path[step]={ coordinate=>[y,x],
  cost=>0,
  next_cost=>(y-end.y)+(x-end.x),
  previous=>path[step-1],
  actual_cost=>cost+next_cost }
  2、另一个数组,下标即是坐标点,指向的匿名哈希存放OPEN、CLOSE、前一节点的状态,如: arr[y][x]->{point},方便回退时能直接获取到上一步的坐标点和状态,数据结构如下:
  arr[y][x]={ flag=>0,
  point=>arr[y-1][x-1] }
  数据结构设计好后,根据上面的伪代码实现还是比较容易:



use strict;
use List::Util;
use constant {WIDTH=>12,HEIGHT=>8,DEBUG=>1,};
my @uldr=( 0,[-1,0],[0,-1],[1,0],[0,1], );  # 上、左、下、右
my @bg=();
for(my $y=0;$y<HEIGHT;$y++){
for( my $x=0 ; $x<WIDTH ; $x++ ){
if( $y == 0 || $y == HEIGHT-1 ||
$x == 0 || $x == WIDTH-1  ){
$bg[$y][$x] = '*';
}
else{
$bg[$y][$x] = ' ';
}
}
}  # 初始化迷宫
my @obstruction=( [1,5],[1,4],[1,3],[2,3],[2,5],[2,6],[3,6],[4,6],[5,6],[3,3],[3,2],[3,1], );  # 障碍物坐标
map{ $bg[ $obstruction[$_][0] ][ $obstruction[$_][1] ] = '#' } 1..$#obstruction-1;
$bg[ $obstruction[0][0] ][ $obstruction[0][1] ] = '@';
@bg=( ['*','*','*','*','*','*','*','*','*','*','*','*',],
['*',' ',' ',' ','#',' ',' ',' ',' ',' ',' ','*',],
['*',' ','#',' ',' ',' ',' ',' ',' ',' ',' ','*',],
['*',' ','#',' ',' ',' ','#',' ',' ',' ',' ','*',],
['*',' ','#',' ',' ',' ','#','#','#','#','#','*',],
['*',' ',' ','#',' ',' ','#',' ',' ',' ',' ','*',],
['*',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',],
['*','*','*','*','*','*','*','*','*','*','*','*',],
);
print @$_,"\n" foreach(@bg);
my @bg_ghost=();  # 0--未经过 1--已走 2--不可通过
print "-"x15,"\n";
sub caclulate_cost{
my ($sp,$ep)=@_;
return abs($sp->[0] - $ep->[0]) + abs($sp->[1] - $ep->[1]);
}
sub handle{
my @path=();  # 存放步数的数组
my $start=[ $obstruction[0][0] , $obstruction[0][1] ];  # 起点
$start=[1,10];
my $end=[ $obstruction[-1][0] , $obstruction[-1][1] ];  # 终点
$end=[6,9];
my ($step,$p_step,$p_gh)=(0,'','');  # 步数、指向数组元素的指针、指向bg_ghost元素的指针
$path[$step]={ coordinate=>[$start->[0],$start->[1]],
cost=>0,
next_cost=>&caclulate_cost( $start,$end ),
previous=>0,
};   # 每一步保存坐标、预计开销、到目的地距离、父节点,起点开销为0
$path[$step]->{actual_cost}=$path[$step]->{cost} + $path[$step]->{next_cost};  # 实际开销
$bg_ghost[ $start->[0] ][ $start->[1] ]->{point}='';   # 起点的父节点为空
while(@path){
$p_step=pop(@path);
print "  step:$step,p_step:$p_step\n" if DEBUG;
if( $p_step->{coordinate}->[0] == $end->[0] &&
$p_step->{coordinate}->[1] == $end->[1] ){   # 到达目的地
my @arr=('A'..'Z','a'..'z');
my @temp=();
while($p_step){
push @temp,$p_step->{coordinate};
$p_step=$p_step->{previous};   # 顺着父节点回溯,获取每个节点
            }
@temp=reverse(@temp);
foreach(0..$#temp){
$bg[ $temp[$_]->[0] ][ $temp[$_]->[1] ] = $arr[$_];
}
return 1;
}  # end if
$step++;
for(my $cnt=1;$cnt<=4;$cnt++){
my $y= $p_step->{coordinate}->[0]+$uldr[$cnt][0] ;
my $x= $p_step->{coordinate}->[1]+$uldr[$cnt][1] ;
print "    ($p_step->{coordinate}->[0],$p_step->{coordinate}->[1])+($uldr[$cnt][0],$uldr[$cnt][1]),(y,x)=($y,$x)\n" if DEBUG;
if( $y < 1 || $y > HEIGHT-2 || $x < 1 || $x > WIDTH-2 || $bg[$y][$x] eq '#' ){
$bg_ghost[$y][$x]->{flag} = 2 ;  # 不可经过
            }
if( ! $bg_ghost[$y][$x]->{flag} ){        # 未经过的   
$bg_ghost[$y][$x]->{flag}=1;        # 设置已经过
$bg_ghost[$y][$x]->{point}=$p_step; # 保存前一节点状态
my $px={  coordinate=>[$y,$x],
cost=>$p_step->{cost}+1,
next_cost=>&caclulate_cost( [$y,$x],$end ),
previous=>$p_step,
};
$px->{actual_cost}=$px->{cost} + $px->{next_cost};
push @path,$px;
}
else{
$p_gh=$bg_ghost[$y][$x]->{point};
print "      p_gh:$p_gh\n" if DEBUG;
if($p_gh && $p_step->{cost}+1 < $p_gh->{cost} ){   # 如果当前开销较小
print "      $p_step->{cost},$p_gh->{cost}\n" if DEBUG;
$p_gh->{cost}=$p_step->{cost}+1;    #
$p_gh->{previous}=$p_step;            # 将前一个节点设置为当前节点之父
$p_gh->{actual_cost}=$p_gh->{cost}+$p_gh->{next_cost};        # 更新前一节点开销
                }
}
}   
$bg_ghost[ $p_step->{coordinate}->[0] ][ $p_step->{coordinate}->[1] ]->{flag}=1;  # 设置已经过
@path=sort{$b->{actual_cost}<=>$a->{actual_cost}}@path;   # 排序,开销最小的放在最后
    }
return 0;
}
&handle;
print @$_,"\n" foreach(@bg);
  计算出来的最短路径:
DSC0000.jpg
  比较一下深度优先算法:
DSC0001.jpg
  
  
  
  


  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-156566-1-1.html 上篇帖子: 功能丰富的Perl:轻松调试Perl的技巧 下篇帖子: 使用Perl写的一个删除HTML代码的函数
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表