0) { // preseed the best cost by a suitably high value that shouldn't occur $bestTourCost = 255; // using dynamic programming, find the cheapest sub-tour joined to the // last landmark foreach ($unvisited as $nextLandmark) { $tour = findOptimalTour($countUnvisited - 1, array_diff($unvisited, array($nextLandmark)), $nextLandmark, $costs); $tourCost = $tour[1] += $costs[$nextLandmark + $lastLandmark]; // find the best tour if ($bestTourCost > $tourCost) { $tour[0][] = $nextLandmark; $bestTour = $tour; $bestTourCost = $tourCost; } } return $bestTour; } else { // return the sub-tour for the start node to the last landmark return array(array(), $costs[1 + $lastLandmark]); } } $landmarkNames = $costs = array(); $landmarks = array(1 => array(deg2rad(51.58701), deg2rad(-0.23029)), 2 => array(deg2rad(52.34139), deg2rad(4.88833))); // As the challenge gives points for lower CRAP index, and not error // handling, assume file is readable rather than checking. $csvHandle = fopen($_SERVER['argv'][1], 'r'); // Trim off the field keys and use them for field lookups. $fields = array_flip(fgetcsv($csvHandle)); $i = 4; // Load each landmark from file. while (($landmark = fgetcsv($csvHandle)) !== false) { $landmarkNames[$i] = $landmark[$fields['Landmark']]; // Convert the latitude and longitude to radians from degrees. $landmark = array(deg2rad($landmark[$fields['Latitude']]), deg2rad($landmark[$fields['Longitude']])) ; // Get the costs for each node to every other node, except start to end // which there is no edge for. foreach ($landmarks as $key => $landmarkB) { // Find a proportional representation of the distance between // landmarks. Exact distance is not needed as each cost would be // multiplied by the same amount. $a = pow(sin(($landmark[0] - $landmarkB[0]) / 2), 2) + cos($landmark[0]) * cos($landmarkB[0]) * pow(sin(($landmark[1] - $landmarkB[1]) / 2), 2); $costs[$key + $i] = atan2(sqrt($a), sqrt(1 - $a)); } // Add the landmark after the cost calculation to prevent calculating an // nonexistant edge. $landmarks[$i] = $landmark; // Use a power of 2 for each key to allow for symetric costs by adding // two keys together. $i *= 2; } fclose($csvHandle); // The remaining landmarks (excluding the start and end). $nodes = array_slice(array_keys($landmarks), 2); $result = findOptimalTour(count($nodes), $nodes, 2, $costs); // Print out the optimial tour landmark names, a foreach would be simpler but // would increase CRAP index. echo join("\n", array_replace(array_fill_keys($result[0], null), $landmarkNames)) . "\n";