15

I have a list of coordinate to be sorted with a spiral algorithm. My need is to start on the middle of the area and "touch" any coordinate.

To simplify this is the representation of the (unsorted) list of coordinates (x,y marked with a "dot" on following image).

CSV list of coordinates is available here.
X increase from left to right
Y increases from TOP to BOTTOM

unsorted list of coordinates Every coordinate is not adjacent to the following one but are instead distanciated by 1 or 2 dice (or more in certain case).

Starting from the center of the area, I need to touch any coordinate with a spiral movement:

spiral approach

to parse each coordinate I've drafted this PHP algorithm:

 //$missing is an associative array having as key the coordinate "x,y" to be touched
 $direction = 'top';
 $distance = 1;
 $next = '128,127';     //starting coordinate
 $sequence = array(
     $next;
 )
 unset($missing[$next]);
 reset($missing);
 $loopcount = 0;
 while ($missing) {
    for ($loop = 1; $loop <= 2; $loop++) {
        for ($d = 1; $d <= $distance; $d++) {
            list($x,$y) = explode(",", $next);
                if ($direction == 'top')    $next = ($x) . "," . ($y - 1);
            elseif ($direction == 'right')  $next = ($x + 1) . "," . ($y);
            elseif ($direction == 'bottom') $next = ($x) . "," . ($y + 1);
            elseif ($direction == 'left')   $next = ($x - 1) . "," . ($y);
            if ($missing[$next]) {
                unset($missing[$next]);     //missing is reduced every time that I pass over a coordinate to be touched
                $sequence[] = $next;
            }
        }
            if ($direction == 'top')    $direction = 'right';
        elseif ($direction == 'right')  $direction = 'bottom';
        elseif ($direction == 'bottom') $direction = 'left';
        elseif ($direction == 'left')   $direction = 'top';
    }
    $distance++;
 }

but as coordinate are not equidistant from each other, I obtain this output:

sorted list of coordinates

As is clearly visible, the movement in the middle is correct whereas and accordingly with the coordinate position, at a certain instant the jump between each coordinate are not anymore coherent.

How can I modify my code to obtain an approach like this one, instead?

expected sorted list of coordinates

To simplify/reduce the problem: Imagine that dots on shown above image are cities that the salesman have to visit cirurarly. Starting from the "city" in the middle of the area, the next cities to be visited are the ones located near the starting point and located on North, East, Soutch and West of the starting point. The salesman cannot visit any further city unless all the adjacent cities in the round of the starting point hadn't been visited. All the cities must be visited only one time.

Stefano Radaelli
  • 1,088
  • 2
  • 15
  • 35
  • Its seems to me your csv isn't right. – Micromega Mar 02 '15 at 18:56
  • @Phpdna, what you mean that isn't right? On csv coords increase on x-axes from left to right and y-axes from top to bottom (on other hands it does not match with a typical cartesian coordinate system but is instead build on 2nd quadrant). Additionally, the coord in the middle of the area does not have [ 0,0 ] but [ 128,127 ] instead. – Stefano Radaelli Mar 03 '15 at 08:41
  • How do you visualize your result? Are you sure there is no bug in the visualization step? And what precisely do you mean by "Every coordinate is not adjacent to the following one but are instead distanciated by 1 or 2 dice (or more in certain case)."? – Fabian Kleiser Mar 04 '15 at 06:37
  • @StefanoRadaelli your problem starts with the 3rd right column (from center) since the y is shifted, in other words, this is not a normal spiral, and you can't apply a normal spiral algorithm on it. – tbc Mar 04 '15 at 12:10
  • @fakeller, every coordinate of the shown above grid is adjacent to next one. As you can see every square is identified by a color (lightblue, yellow, green). On top of that on green squares there's a "black dot" identifying the coordinate of my attachment. Basically it means that I need to sort the coordinate of the attachment (representing the dots on above shown image) in a spiral way. The result with red segment has been "simulated" manually with an image editor. I don't search EXACTLY such output but instead something similar (I mean something describing a spiral movement). – Stefano Radaelli Mar 04 '15 at 14:49
  • @tbc you're right. But that's exactly my problem. I would like to find an algorithm capable to reproduce a "spiral movement" (like the one shown manually with the red segments) even if the coordinates does not describe a perfect spirla. – Stefano Radaelli Mar 04 '15 at 14:49
  • Is it a constraint that you first have to move upward and then turn for a left spiral? – Fabian Kleiser Mar 04 '15 at 16:02
  • @FaKeller. No it's not. – Stefano Radaelli Mar 09 '15 at 12:50

2 Answers2

10

Algorithm design

First, free your mind and don't think of a spiral! :-) Then, let's formulate the algorithms constraints (let's use the salesman's perspective):

I am currently in a city and am looking where to go next. I'll have to find a city:

  • where I have not been before
  • that is as close to the center as possible (to keep spiraling)
  • that is as close as possible to my current city

Now, given these three constraints you can create a deterministic algorithm that creates a spiral (well at least for the given example it should, you probably can create cases that require more effort).

Implementation

First, because we can walk in any direction, lets generally use the Euclidean distance to compute distances.

Then to find the next city to visit:

$nextCost = INF;
$nextCity = null;
foreach ($notVisited as $otherCity) {
    $cost = distance($current_city, $other_city) + distance($other_city, $centerCity);
    if ($cost < $nextCost) {
        $nextCost = $cost;
        $nextCity = $otherCity;
    }
}
// goto: $nextCity

Just repeat this until there are no more cities to visit.

To understand how it works, consider the following picture:

enter image description here

I am currently at the yellow circle and we'll assume the spiral up to this point is correct. Now compare the length of the yellow, pink and blue lines. The length of those lines is basically what we compute using the distance functions. You will find that in every case, the next correct city has the smallest distance (well, at least as long as we have as many points everywhere, you probably can easily come up with a counter-example).

This should get you started to implement a solution for your problem.

(Correctness) Optimization

With the current design, you will have to compare the current city to all remaining cities in each iteration. However, some cities are not of interest and even in the wrong direction. You can further optimize the correctness of the algorithm by excluding some cities from the search space before entering the foreach loop shown above. Consider this picture:

enter image description here

You will not want to go to those cities now (to keep spiraling, you shouldn't go backwards), so don't even take their distance into account. Albeit this is a little more complicated to figure out, if your data points are not as evenly distributed as in your provided example, this optimization should provide you a healthy spiral for more disturbed datasets.

Update: Correctness

Today it suddenly struck me and I rethought the proposed solution. I noticed a case where relying on the two euclidean distances might yield unwanted behavior:

enter image description here

It is easily possible to construct a case where the blue line is definitely shorter than the yellow one and thus gets preferred. However, that would break the spiral movement. To eliminate such cases we can make use of the travel direction. Consider the following image (I apologize for the hand-drawn angles):

enter image description here

The key idea is to compute the angle between the previous travel direction and the new travel direction. We are currently at the yellow dot and need to decide where to go next. Knowing the previous dot, we can obtain a vector representing the previous direction of the movement (e.g. the pink line).

Next, we compute the vector to each city we consider and compute the angle to the previous movement vector. If that vector is <= 180 deg (case 1 in the image), then the direction is ok, otherwise not (case 2 in the image).

// initially, you will need to set $prevCity manually
$prevCity = null;

$nextCost = INF;
$nextCity = null;
foreach ($notVisited as $otherCity) {
    // ensure correct travel direction
    $angle = angle(vectorBetween($prevCity, $currentCity), vectorBetween($currentCity, $otherCity));
    if ($angle > 180) {
        continue;
    }

    // find closest city
    $cost = distance($current_city, $other_city) + distance($other_city, $centerCity);
    if ($cost < $nextCost) {
        $nextCost = $cost;
        $nextCity = $otherCity;
    }
}
$prevCity = $currentCity;
// goto: $nextCity

Pay attention to compute the angle and vectors correctly. If you need help on that, I can elaborate further or simply ask a new question.

Fabian Kleiser
  • 2,988
  • 3
  • 27
  • 44
  • :What has this to do with the question, I.e the grid is evenly spaced and there aren't cities!? – Micromega Mar 05 '15 at 23:22
  • 1
    The cities originate from the metaphor of the traveling salesman introduced in the question, to simplify the language and understanding. And if you look closely at the grid, the points are not evenly distributed (i.e. at the top left there are two points that are directly adjacent). The points in the example do only contain integer values as coordinates, however the approach is not limited to that. – Fabian Kleiser Mar 06 '15 at 07:20
  • :I understand that but he has a csv with the points, AFAIK the points in the csv are evenly spaced? Is that right? – Micromega Mar 06 '15 at 07:37
  • No, they are not evenly spaced. – Fabian Kleiser Mar 06 '15 at 08:15
  • The salesman analogy refers to the points as cities. The algorithm needs to connect the dots, e.g. the salesman needs to visit the cities. If you do not know where the traveling salesman comes from, please refer to the [traveling salesman problem](http://en.wikipedia.org/wiki/Travelling_salesman_problem). – Fabian Kleiser Mar 06 '15 at 09:42
  • :I downvoted you because the travelsalesman problem is to find the shortest route not to travel in spiral and also euklidian distance is only a litte problem of the travelman salesman problem. – Micromega Mar 06 '15 at 09:47
  • 1
    @Phpdna I wasn't searching for an algorithm to solve the Traveling Salesman Problem. Just imagine that dots on image are cities and an hypotetic Traveling Salesman has to visit each city (only one time) moving circurarly. Maybe the "Traveling Salesman" definition is fuorviant but my request is not related to the TSP approach to solve the issue that I have. – Stefano Radaelli Mar 06 '15 at 14:22
  • I'm not sure why this answer is give upvote are accepted. Its really totally vague. BTW. the eulidian distance of the rounded corner then the straight line. – Micromega Mar 06 '15 at 15:22
  • 1
    @Stefano, I added a section explaining how to further ensure correctness. – Fabian Kleiser Mar 06 '15 at 15:55
1

The problem seems to be in the if-conditional when you missing traverse a co-ordinate, I.e because of rounding of the corners. A else conditional with a reverse to the previous calculation of the co-ordinate would fix it.

Micromega
  • 12,486
  • 7
  • 35
  • 72
  • That's a good starting point. The shown routing has been drawn manually with an image editor only to show "a possible output expected". My request is to find an algorithm that describe a spiral movement even if the coordinate are not describing a perfect spiral. The expected output may be different from shown one but have to be similar in terms of routing computed. – Stefano Radaelli Mar 04 '15 at 14:57
  • @StefanoRadelli:You overthink it. Make it simple and use a 3d to 2d projections. – Micromega Mar 04 '15 at 15:06