Skip to content

Instantly share code, notes, and snippets.

@dragon0
Last active September 14, 2015 18:53
Show Gist options
  • Save dragon0/39e5b8a06cd33b147806 to your computer and use it in GitHub Desktop.
Save dragon0/39e5b8a06cd33b147806 to your computer and use it in GitHub Desktop.
ACM Airports problem
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ACMAirport {
// Earth's radius
public static final double R = 3958.75;
/**
* Represents the Latitude and Longitude of a point on Earth's surface,
* in radians.
*
*/
private static class Coordinate{
private final double lat;
private final double lon;
public Coordinate(int latDeg, int latMin, int latSec, String ns, int lonDeg, int lonMin, int lonSec, String ew){
// latitude is given in degrees, minutes, seconds; convert to decimal
double lat = latDeg;
lat += latMin / 60.0;
lat += latSec / (60.0 * 60.0);
// adjust sign
switch(ns){
case "n":
case "N":
break;
case "s":
case "S":
lat = -lat;
break;
default:
throw new IllegalArgumentException("ns must be n or s, got " + ns);
}
// store
this.lat = Math.toRadians(lat);
// likewise
double lon = lonDeg;
lon += lonMin / 60.0;
lon += lonSec / (60.0 * 60.0);
switch(ew){
case "e":
case "E":
break;
case "w":
case "W":
lon = -lon;
break;
default:
throw new IllegalArgumentException("ew must be e or w, got " + ew);
}
this.lon = Math.toRadians(lon);
}
public double getLon() {
return lon;
}
public double getLat() {
return lat;
}
}
// map from airport code to airport's location
private static final Map<String, Coordinate> airportLocations = new HashMap<>();
/**
* Insert an airport into airportLocations.
*
* line contains:
* the airport code;
* degrees, minutes, seconds latitude;
* and degrees, minutes, seconds longitude.
* eg "MAD 40 28 20 N 03 33 39 W"
*
* @param line A String containing the airport code
*/
private static void insert(String line){
// Using a regex would probably allow for better error checking
String[] tokens = line.split(" ");
String code = tokens[0];
int latDeg = Integer.parseInt( tokens[1]);
int latMin = Integer.parseInt( tokens[2]);
int latSec = Integer.parseInt( tokens[3]);
String ns = tokens[4];
int lonDeg = Integer.parseInt( tokens[5]);
int lonMin = Integer.parseInt( tokens[6]);
int lonSec = Integer.parseInt( tokens[7]);
String ew = tokens[8];
Coordinate c = new Coordinate(latDeg, latMin, latSec, ns, lonDeg, lonMin, lonSec, ew);
airportLocations.put(code, c);
}
/**
* Read test data from System.in
* @param args
*/
public static void main(String[] args) {
String data =
"MAD 40 28 20 N 03 33 39 W\n"
+"CMN 33 22 04 N 07 35 16 W\n"
+"RAK 31 36 24 N 08 02 10 W\n"
+"HNL 21 18 57 N 157 55 36 W\n"
+"YYZ 43 40 38 N 79 37 50 W\n"
+"FRA 50 01 35 N 08 32 35 E\n"
+"LAS 36 04 49 N 115 09 08 W\n"
+"LAX 33 56 33 N 118 24 29 W\n"
+"---\n"
+"LAS LAX HNL LAX LAS\n"
+"LAX YYZ FRA CMN\n"
+"LAX MAD RAK\n"
;
/*
MAD 40 28 20 N 03 33 39 W
CMN 33 22 04 N 07 35 16 W
RAK 31 36 24 N 08 02 10 W
HNL 21 18 57 N 157 55 36 W
YYZ 43 40 38 N 79 37 50 W
FRA 50 01 35 N 08 32 35 E
LAS 36 04 49 N 115 09 08 W
LAX 33 56 33 N 118 24 29 W
---
LAS LAX HNL LAX LAS
LAX YYZ FRA CMN
LAX MAD RAK
*/
Scanner in = new Scanner(System.in);
//Scanner in = new Scanner(data);
String line;
// create airport location table
while(in.hasNextLine() && !(line = in.nextLine()).equals("---")){
insert(line.trim());
}
// build a thread pool to compute distances concurrently
// C++ can omit this
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService pool = Executors.newFixedThreadPool(cores);
CompletionService<Double> cs = new ExecutorCompletionService<>(pool);
List<Future<Double>> results = new LinkedList<>();
// read paths and begin computing distances
while(in.hasNextLine()){
DistanceTask task = new DistanceTask(in.nextLine().trim());
// executes task.call() in tread pool
// C++ should just do cout << task.call() << endl;
results.add(cs.submit(task));
}
// results are read in the same order they are submitted
for(Future<Double> result : results){
try {
System.out.println(Math.round(result.get()));
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
pool.shutdown();
in.close();
}
private static class DistanceTask implements Callable<Double>{
// space-delimited list of airport codes
private final String line;
public DistanceTask(String line) {
this.line = line;
}
/**
* Computes the total distance of a list of airport connections
*
*/
@Override
public Double call() throws Exception {
String[] airports = line.split(" ");
double distance = 0.0;
Coordinate prevCoord = null;
Coordinate curCoord = airportLocations.get(airports[0]);
for(int i = 1; i < airports.length; i++){
prevCoord = curCoord;
curCoord = airportLocations.get(airports[i]);
distance += greatCircleDistance(prevCoord, curCoord);
}
return distance;
}
/**
* Calculate the Great-Circle distance between from and to.
* https://en.wikipedia.org/wiki/Great-circle_distance
*
* @param from latitude and longitude of departure
* @param to latitude and longitude of destination
* @return the great-circle distance
*/
private double greatCircleDistance(Coordinate from, Coordinate to) {
double deltalon = to.getLon() - from.getLon();
double theta = Math.acos(
Math.sin(from.getLat()) * Math.sin(to.getLat())
+ Math.cos(from.getLat()) * Math.cos(to.getLat()) * Math.cos(deltalon));
return R * theta;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment