Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A* implementation in PHP validation

This is a code that I got from the site here and I'd like to know whether this implementation of A* is correct. I have looked at it and compare it with the wikipedia page and it seems valid.. The reason why I ask is because in the site it says there is still a bug in this code, I tried finding it but can't find any. I want to change it though so that it takes an origin and destination as input parameter

<?php

class AStarSolver
{
    function solve(&$s)
    {
        include_once('PQueue.class.php');
        $o = new PQueue();
        $l = array();
        $c = array();
        $p = array();
        $a = $s->getStartIndex();
        $z = $s->getGoalIndex();
        $d = $s->goalDistance($a);

        $n0 = array('g'=>0, 'h'=>$d, 'i'=>$a, 'p'=>NULL, 'f'=>$d);
        $o->push($n0, -$d);
        $l[$a] = TRUE;

        while (! $o->isEmpty())
        {
            $n = $o->pop();

            if ($n['i'] == $z)
            {
                while ($n)
                {
                    $p[] = $n['i'];
                    $n = $n['p'];
                }
                break;
            }

            foreach ($s->getNeighbors($n['i']) as $j => $w)
            {
                if ((isset($l[$j]) || isset($c[$j])) && isset($m) && $m['g'] <= $n['g']+$w)
                    continue;

                $d = $s->goalDistance($j);
                $m = array('g'=>$n['g']+$w, 'h'=>$d, 'i'=>$j, 'p'=>$n, 'f'=>$n['g']+$w+$d);

                if (isset($c[$j]))
                    unset($c[$j]);

                if (! isset($l[$j]))
                {
                    $o->push($m, -$m['f']);
                    $l[$j] = TRUE;
                }
            }
            $c[$n['i']] = $n;
        }
        return $p;
    }

}

?>

The code to the Pqueue can be found here

like image 381
aherlambang Avatar asked Feb 26 '11 05:02

aherlambang


People also ask

Can validation be done in PHP?

Form Validation is a necessary process before the data entered in the form is submitted to the database. This is done to avoid unnecessary errors. In PHP Form validation, the script checks for data in respective fields based on the rules set by the developer, and returns an error if it does not meet the requirements.

Which are the types of validation in PHP?

Client-Side Validation: This type of validation is done on the web browser of the client machine. Server-Side Validation: The data on submission is then sent to the server machine to perform validation checks there.

How do you validate a form in PHP explain it with an example?

$_SERVER["PHP_SELF"] exploits can be avoided by using the htmlspecialchars() function. The form code should look like this: <form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>"> The exploit attempt fails, and no harm is done!


1 Answers

The site suggests that the bug might be in the PQueue class.

In PQueue::pop this

$j+1 < $m

is a test whether the heap node at $i has one child (at $j) or two (at $j and $j+1).

But $m here is count($h) only on the first iteration through the loop, since the --$m in the loop condition is evaluated every time.

Move that --$m next to the array_pop where it belongs, and that will be one less bug.


Now for AStarSolver.

The variables are (relative to Wikipedia pseudocode):

  • $o – open set as priority queue;
  • $l – open set as map keyed by index;
  • $c – closed set as map keyed by index;
  • $n – current node (x);
  • $m – neighbour node (y) ?;
  • $j – neighbour node index.

Problems that I see:

  • $n = $o->pop() isn't followed by unset($l[$n['i']]). Since both $o and $l represent the same set, they should be kept in sync.

  • According to Wikipedia the closed set is only used if the heuristic is monotone (and I think a distance heuristic is monotone), and in that case, once a node is added to the closed set, it is never visited again. This code seems to implement some other pseudocode, which does remove nodes from the closed set. I think this defeats the purpose of the closed set, and the first condition in the inner loop should be

    if (isset($c[$j]) || isset($l[$j]) && isset($m) && $m['g'] <= $n['g']+$w)

    Then we can remove the unset($c[$j]).

  • $m['g'] in this condition should be the g-score of the current neighbour indexed by $j. But $m has whatever value is left over from the previous loop: the node corresponding to $j on a previous iteration.

    What we need is a way to find a node and its g-score by node index. We can store the node in the $l array: instead of $l[$j] = TRUE we do $l[$j] = $m and the above condition becomes

    if (isset($c[$j]) || isset($l[$j]) && $l[$j]['g'] <= $n['g']+$w)

  • Now the tricky bit. If the node we just found is not in the open set, we add it there (that's the $o->push and $l[$j] =).

    However, if it is in the open set we just found a better path to it, so we must update it. The code doesn't do this and it's tricky because the priority queue doesn't provide a routine for increasing the priority of an element. However, we can rebuild the priority queue completely and the last bit of code in the inner loop becomes

    if (! isset($l[$j])) {
       $o->push($m, -$m['f']);
       $l[$j] = $m; // add a new element
    } else {
       $l[$j] = $m; // replace existing element
       $o = new PQueue();
       foreach ($l as $m)
          $o->push($m, -$m['f']);
    }

    This is not terribly efficient, but it's a starting point. Changing an element in a priority queue isn't efficient anyway, because you first have to find it.


Even without these changes the algorithm does find a path, just not the best path. You can see it in the mentioned mazes:

  • In the crazy maze in the third inner circuit: the taken upper path around is slightly longer than the lower path would have been because of the obstacles on the left.

  • In the big maze in the upper-right part of the path there's an unnecessary loop up.


Since this was on my mind, I implemented my own version of the algorithm and posted it in an answer to your previous question.

like image 55
aaz Avatar answered Oct 24 '22 11:10

aaz