5
3
Situation:
- A turtle starts at (0, 0) on a cartesian graph.
- We have a non-empty zero-indexed "moves" list that contains numbers.
- Each number represents the distance moved.
- The first number is the distance north, the second is the distance east, the third is the distance south, the fourth is the distance west, and repeats like this forever. Therefore the directions cycle every four moves.
Goal:
Find an algorithm that gives the move number that makes the turtle cross a point that it has already visited before.
The move number is the index of the "moves" list.
This algorithm should preferably be O(n).
Example:
If given this move list: [1, 3, 2, 5, 4, 4, 6, 3, 2]
The move number answer is then 6. (It's the 7th move).
Draw it on a graph, the turtle will go:
(0,0) -> (0,1) -> (3,1) -> (3,-1) -> (-2,-1) -> (-2,3) -> (2,3) -> (2,-3)
At this 6 move number (7th move) it will meet (2,1) which is a point that the turtle has already crossed.
Example Implementation
Here was my try in PHP (I think it's kind of like O(n*(m+m)) where m is the average size of the distance):
/**
* Logo Turtle Challenge
* This solution is somewhat space inefficient, and could become time inefficient if the distance range for a particular move is huge!
* There is probably a solution that allows you to persist only the corner coordinates and then using line equations to figure out if they intersect.
* The space inefficiency could be partially solved with a lazy range generator.
* Could be further micro-optimised by only checking the coordinates on the fourth[3] movement.
*/
function logo_turtle($movements) {
$coordinates = [0 => [0 => true]];
$current_coordinate = [
'x' => 0,
'y' => 0
];
$directions = ['n', 'e', 's', 'w'];
foreach($movements as $move => $distance){
// cycle through the directions
$current_direction = array_shift($directions);
array_push($directions, $current_direction);
// if the distance is less than 1, then skip it, but we still cycle the direction!
if ($distance < 1) {
continue;
}
// clean slate
$coordinates_to_check = [];
// for each direction, acquire a list of coordinates derived from its traverse
// these coordinates will be used to check if turtle crosses its own previous path
switch($current_direction){
case 'n':
$x_end = $current_coordinate['x'];
$y_end = $current_coordinate['y'] + $distance;
$start = $current_coordinate['y'] + 1;
foreach(range($start, $y_end) as $value){
$coordinates_to_check[] = [ 'x' => $current_coordinate['x'], 'y' => $value];
}
break;
case 'e':
$x_end = $current_coordinate['x'] + $distance;
$y_end = $current_coordinate['y'];
$start = $current_coordinate['x'] + 1;
foreach(range($start, $x_end) as $value){
$coordinates_to_check[] = [ 'x' => $value, 'y' => $current_coordinate['y']];
}
break;
case 's':
$x_end = $current_coordinate['x'];
$y_end = $current_coordinate['y'] - $distance;
$start = $current_coordinate['y'] - 1;
foreach(range($start, $y_end) as $value){
$coordinates_to_check[] = [ 'x' => $current_coordinate['x'], 'y' => $value];
}
break;
case 'w':
$x_end = $current_coordinate['x'] - $distance;
$y_end = $current_coordinate['y'];
$start = $current_coordinate['x'] - 1;
foreach(range($start, $x_end) as $value){
$coordinates_to_check[] = [ 'x' => $value, 'y' => $current_coordinate['y']];
}
break;
}
// if the traversal meets one of the existing coordinates, then the current move is an interception
foreach($coordinates_to_check as $xy){
if(
isset(
$coordinates[
$xy['x']
][
$xy['y']
]
)
){
return $move;
}
// add the point to the list of coordinates that has been traversed
$coordinates[$xy['x']][$xy['y']] = true;
}
// update the current coordinate for the next iteration
$current_coordinate = [
'x' => $x_end,
'y' => $y_end
];
}
return -1;
}
$moves = [1, 3, 2, 5, 4, 4, 6, 3, 2];
$turtle_move = logo_turtle($moves); // returns 6
Criteria:
- Feel free to write it in any language you want.
- Winning algorithm is the first one that is published with an O(n) implementation.
- With regards to algorithms with the same time complexity class, whichever code has the least amount of characters win.
- Time limit is the best answer by 30th November 2014.
For example, given array A as defined above, the function should return 7, because the turtle touches its previous path at point (2,1) in the 7th move. Maybe it's newer task description, but now it asks to return 7 instead of 6. – Oleg Abrazhaev – 2017-01-23T07:22:16.153
1This is under-specified. Be especially clear in the winning criteria. How exactly is it scored? And the rest could be written more… "nicely". – Ingo Bürk – 2014-11-01T17:47:11.277
@IngoBürk Ok I wrote it more clearly and added specifications for winning criteria. Is that alright, what else should I add? – CMCDragonkai – 2014-11-02T05:31:03.527
The winning criteria are still unclear. What are these "extra points" you're mentioning? How many do we get and how exactly do they add to the score, since the score is a complexity class? Also, this is likely to end in ties, so you should have a tie breaker. – Ingo Bürk – 2014-11-02T07:55:08.117
How would you suggest to score time complexity classes? – CMCDragonkai – 2014-11-02T10:51:52.570
Are the moves always integers? If so, an extremely easy way to solve this is just to plot the path on a grid, and stop when you find a white pixel. O(n). – Nathaniel – 2014-11-03T06:38:20.617
Yes they are always integers. Are you sure that's O(n), it sounds like for every move you have to check the grid. Isn't that what I did in the PHP implementation anyway? – CMCDragonkai – 2014-11-03T07:08:51.810
@CMCDragonkai querying an array is a constant time operation – Sparr – 2014-11-04T00:31:34.060
1Well yes, but what are we talking about at this point? The array of grid points or the array of move points? Because I've used both. On every move, I query the grid points based on the move points. This is not really O(n) because on long distance moves, you'd have to check move point and it's hence O(n * m) where m is the average distance of moves. – CMCDragonkai – 2014-11-04T05:04:42.847