人材獲得作戦・4 試験問題ほか
さて試験問題です。内容は、壁とスペースで構成された迷路が与えられたとき、スタート地点からゴール地点に至る最短経路を求めよ、というものです。
とりあえず Perl で書いてみました。正確な時間は測っていないけれど(途中で熱中して時間のことを完全に忘れていた)、多分1時間くらいかな? 最後の20分は見栄えの調整だったような気がしますが(汗) せっかく Perl を使ったのに Perl っぽくないというか C 言語っぽい香りがします。何故だろう。
#!/usr/bin/perl my ($maze); my ($startX, $startY); # Start position my ($mazeSX, $mazeSY); # Size of maze sub { $mazeSY = 0; foreach (<DATA>) { $x = 0; foreach (split //) { ($startX, $startY) = ($x, $mazeSY) if /S/; $maze->{$x}->{$mazeSY} = $_; $x++; } $mazeSY++; } $mazeSX = $x; }->('Initialize maze field from __DATA__'); my ($goalX, $goalY); # Goal position sub { $maze->{$startX}->{$startY} = $step = 1; while (1) { for my $y (0..$mazeSY) { for my $x (0..$mazeSX) { next if $maze->{$x}->{$y} != $step; for (0..3) { my $xx = $x + ( 0,+1, 0,-1)[$_]; my $yy = $y + (-1, 0,+1, 0)[$_]; $_ = $maze->{$xx}->{$yy}; if (/G/) { ($goalX, $goalY) = ($xx, $yy); return; # Finish searching } elsif (/ / || $step < $_) { $maze->{$xx}->{$yy} = $step + 1; # Fill with step count } } } } $step++; } }->('Find the shortest route to each point'); sub { while ($startX != $goalX || $startY != $goalY) { for (0..3) { my $xx = $goalX + ( 0,+1, 0,-1)[$_]; my $yy = $goalY + (-1, 0,+1, 0)[$_]; if ($maze->{$xx}->{$yy} == $step) { $maze->{$xx}->{$yy} = '$'; ($goalX, $goalY) = ($xx, $yy); last; } } $step--; } $maze->{$startX}->{$startY} = 'S'; }->('Fill the shortest route to goal'); sub { for my $y (0..$mazeSY) { for my $x (0..$mazeSX) { print int $maze->{$x}->{$y} ? ' ' : $maze->{$x}->{$y}; } } }->('Print maze'); __DATA__ ************************** *S* * * * * * * ************* * * * * ************ * * * * ************** *********** * * ** *********************** * * G * * * *********** * * * * ******* * * * * * **************************
処理ごとに関数で括っています。迷路を初期化→各マスまでの最短到達距離を求める→ゴールからスタートまでマーキング→画面に出す、という感じ。アルゴリズムの肝は最短到達距離を求める部分で、A*(エースター)だとか αβ 枝狩りだとか色々テクニックはあるらしいんですが、多分、使っていません。普通にゼロからロジックを考えて実装しました。なので、アルゴリズムを知っている方が有利だとか、というのは多分無い。理論的に考える能力があって、時間をかければ誰でも解ける問題ですね。というわけで、ぐだぐだ言ってる奴はコード書けよハゲ。なかなか面白かった。
えーっと、話を戻して最短到達距離の求め方は、1) 隣のマスに移動すると歩数+1、2) 既に到達したマスでも、到達距離が今の方が短ければ、その値でアップデート、というルールだけで、これを盤面全体でぐるーっと回してやれば、盤面上の各点に移動するための最短距離が得られます。ゴールに関係なく、盤面全体で処理しているので効率悪いけど。
グローバル変数と変数の使いまわしはexcuse。