Created
August 1, 2015 22:02
-
-
Save rsnape/53bac7bbd979ae8ffba6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from math import radians, sin, cos, sqrt, asin, factorial | |
from itertools import combinations, permutations | |
# by average circumference instead of authalic radius (assume planes) | |
# shouldn't actually matter for our purposes as we're using this to generate weights | |
# been nodes/vertices. | |
EARTH_RADIUS_KM = 6372.8 | |
def haversine(lat1, long1, lat2, long2, planet_radius=EARTH_RADIUS_KM): | |
"""Return the shortest distance between two coordinates on a sphere. | |
This does "as the crow flies." We don't need to be 100% accurate, just return | |
a reasonable weight for the path. If this was used for flight planning, there's a bunch | |
of other factors at play (geopolitical boundaries, weather, curved path could be shorter). | |
Use the geographic coordinate system to get weights for pairs of graticules. | |
Convert difference between [l1, l2] lat,long to radians. | |
translated from | |
https://en.wikipedia.org/wiki/Haversine_formula | |
""" | |
# get distance between the points | |
# use vars here to shorten below calc. doing radian of each would be +2 vars | |
phi_Lat = radians(lat2 - lat1) | |
phi_Long = radians(long2 - long1) | |
lat1 = radians(lat1) | |
lat2 = radians(lat2) | |
# haversin(lat2-lat1) + (cos(lat1) * cos(la2) * haversin(long2-long1) | |
a = sin(phi_Lat/2)**2 + \ | |
cos(lat1) * cos(lat2) * \ | |
sin(phi_Long/2)**2 | |
c = 2 * asin(sqrt(a)) | |
return planet_radius * c | |
def generate_weights(data): | |
graph_edges = {} | |
# initialize the keyed dict of lists: | |
for city in data: | |
graph_edges[city[0]] = [] | |
for city0, city1 in permutations(data, 2): | |
weight = haversine( | |
city0[1][0], | |
city0[1][1], | |
city1[1][0], | |
city1[1][1], | |
EARTH_RADIUS_KM | |
) | |
graph_edges[city0[0]].append((city1[0], weight)) | |
return graph_edges | |
def calculate_number_edges(graph_length): | |
# Graph is G = ( Vertice, Edge ) | |
# G = (Vn * Vn - 1) / 2) | |
_l = (graph_length * (graph_length -1)) / 2 | |
if _l < 1: _l = 1 | |
return _l | |
def calculate_shortest_trip(graph, start_and_end_vertex, iteration_limit=None, DEBUG=True): | |
"""Calculate the shortest trip through all points in graph starting and ending at start_and_end_vertex | |
This does a weighted depth-search. | |
Start at start_and_end_vertex. When we traverse an edge, we choose the next vertex. | |
We don't visit the same city twice, but we do have a repeat for the start_and_end_vertex. | |
""" | |
# Exit if we've looped more times than solutions exist | |
iterations = 0 | |
if not iteration_limit: | |
iteration_limit = float("inf") | |
# Return these two. These are for the total trip, not for vertex to vertex traversals. | |
lowest_weight_circuit_cost = float("inf") # initialize to infinity any solution is better than none | |
lowest_weight_circuit = [] | |
# current_circuit_cost = 0 | |
#current_circuit_history = [] | |
# Add the start_and_end_vertex to the list to prevent a loop. | |
#current_circuit_history.append(start_and_end_vertex) | |
# put the path and distance so far onto the stack | |
stack = [([start_and_end_vertex],0)] | |
# stack is empty when all vertices have been seen. | |
while stack: | |
if DEBUG: | |
print("=> Stack status: %s" % len(stack)) | |
# safety in case we somehow start looping. raise StopIteration instead of limiting while so caller knows why | |
iterations += 1 | |
if iterations >= iteration_limit: | |
raise StopIteration('Number of iterations exceeded limit passed: ' + str(iteration_limit)) | |
# get the candidate edges off the top of the stack from our current city. | |
path, dist = stack.pop() | |
# print(path) | |
last_city = path[-1] | |
if len(path) < len(graph): | |
for (next_city, flight_dist) in graph[last_city]: | |
if next_city in path: | |
pass | |
else: | |
newpath = path + [next_city] | |
#optimisations possible here - only add to stack if dist + flight_dist <= lowest_weight_circuit_cost | |
#other optimisation - add the paths ordered by path length, so likely to hit short ones early | |
stack.append((newpath, dist + flight_dist)) | |
else: | |
#We've got a complete path - just add on the "distance home" and test | |
for dest in graph[last_city]: | |
if dest[0] == start_and_end_vertex: | |
circuit_length = dist + dest[1] | |
path.append(start_and_end_vertex) | |
if DEBUG: | |
print('Found a complete path '+str(path) + ' cost ='+ str(circuit_length)) | |
# for when we iter over each possible trip | |
if circuit_length <= lowest_weight_circuit_cost: | |
lowest_weight_circuit_cost = circuit_length | |
lowest_weight_circuit = path | |
return (lowest_weight_circuit, lowest_weight_circuit_cost) | |
def backend_python(data): | |
# incoming data looks like: | |
# ('new york, ny, usa', [40.734838, -73.990810]) | |
# ('portland, me, usa', [43.657727, -70.254636]) | |
# ('seattle, wa, usa', [47.620410, -122.349368]) | |
# ('san fransciso, ca, usa', [37.759936, -122.415058]) | |
# keep the generated lists inside whatever language we're using. | |
# in python, keep it in python lists. | |
# it's going to be far cheaper for this to stay in the language | |
# e.g. c++ won't have to go to python or convert data types | |
# data that comes out of this function is as immediately follows | |
#graph_vertices_and_edges = generate_weights(data) | |
graph_vertices_and_edges = { | |
'seattle, wa, usa': [ | |
('new york, ny, usa', 3867.6996796026383), | |
('portland, me, usa', 3998.364023733441), | |
('san fransciso, ca, usa', 1096.7574923438176) | |
], | |
'san fransciso, ca, usa': [ | |
('new york, ny, usa', 4131.2260736717235), | |
('portland, me, usa', 4373.47227136685), | |
('seattle, wa, usa', 1096.7574923438176) | |
], | |
'new york, ny, usa': [ | |
('portland, me, usa', 447.6466430279615), | |
('seattle, wa, usa', 3867.6996796026383), | |
('san fransciso, ca, usa', 4131.2260736717235) | |
], | |
'portland, me, usa': [ | |
('new york, ny, usa', 447.6466430279615), | |
('seattle, wa, usa', 3998.364023733441), | |
('san fransciso, ca, usa', 4373.47227136685) | |
] | |
} | |
iteration_limit = calculate_number_edges(len(graph_vertices_and_edges)) | |
result = calculate_shortest_trip(graph_vertices_and_edges, 'new york, ny, usa', factorial(len(graph_vertices_and_edges)), DEBUG=False) | |
return result | |
res = backend_python(None) | |
print('Best result is '+str(res)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's a larger dataset:
test_cities_weight_15 = {'portland, me, usa': [('new york, ny, usa', 447.6466430279615), ('seattle, wa, usa', 3998.364023733441), ('san fransciso, ca, usa', 4373.47227136685), ('denver, co, usa', 2895.861939844122), ('austin, tx, usa', 2840.9300348922857), ('montreal, qc, ca', 332.68393556733724), ('vancouver, bc, ca', 4012.0439582695217), ('ancorage, ak, usa', 5351.314013574186), ('toronto, on, ca', 734.3534731575957), ('berlin, germany', 5937.266858217603), ('frankfurt, germany', 5758.907654843056), ('hamburg, germany', 5682.561366723863), ('cologne, germany', 5609.995951156987), ('dublin, ireland', 4671.044334218768)], 'frankfurt, germany': [('new york, ny, usa', 6201.852040536504), ('portland, me, usa', 5758.907654843056), ('seattle, wa, usa', 8181.7466480361), ('san fransciso, ca, usa', 9137.255927375238), ('denver, co, usa', 8118.555563804856), ('austin, tx, usa', 8530.618772237909), ('montreal, qc, ca', 5846.07933033826), ('vancouver, bc, ca', 8053.506529112649), ('ancorage, ak, usa', 7492.129948043919), ('toronto, on, ca', 6335.256241201341), ('berlin, germany', 423.6316360409979), ('hamburg, germany', 392.87849479935215), ('cologne, germany', 152.46723305665677), ('dublin, ireland', 1087.8776732761144)], 'montreal, qc, ca': [('new york, ny, usa', 531.2020828945784), ('portland, me, usa', 332.68393556733724), ('seattle, wa, usa', 3676.5204899041464), ('san fransciso, ca, usa', 4086.2057553148447), ('denver, co, usa', 2632.54876647224), ('austin, tx, usa', 2697.4254785041767), ('vancouver, bc, ca', 3686.260543142142), ('ancorage, ak, usa', 5025.402295871271), ('toronto, on, ca', 505.05332964971956), ('berlin, germany', 5999.59094399283), ('frankfurt, germany', 5846.07933033826), ('hamburg, germany', 5744.95620629242), ('cologne, germany', 5695.368791681914), ('dublin, ireland', 4761.172873284123)], 'cologne, germany': [('new york, ny, usa', 6053.3583286022), ('portland, me, usa', 5609.995951156987), ('seattle, wa, usa', 8038.878710196924), ('san fransciso, ca, usa', 8990.289091553695), ('denver, co, usa', 7966.660997681989), ('austin, tx, usa', 8378.98371782052), ('montreal, qc, ca', 5695.368791681914), ('vancouver, bc, ca', 7911.885681703947), ('ancorage, ak, usa', 7378.093972006955), ('toronto, on, ca', 6184.024494367389), ('berlin, germany', 477.43909710717696), ('frankfurt, germany', 152.46723305665677), ('hamburg, germany', 356.6633710260719), ('dublin, ireland', 939.6336729145872)], 'ancorage, ak, usa': [('new york, ny, usa', 5410.1446563811905), ('portland, me, usa', 5351.314013574186), ('seattle, wa, usa', 2306.7618804760577), ('san fransciso, ca, usa', 3227.9327095873814), ('denver, co, usa', 3854.9535418964065), ('austin, tx, usa', 5096.370282889052), ('montreal, qc, ca', 5025.402295871271), ('vancouver, bc, ca', 2134.1966806947084), ('toronto, on, ca', 4877.203369569424), ('berlin, germany', 7284.160939006664), ('frankfurt, germany', 7492.129948043919), ('hamburg, germany', 7133.384628965154), ('cologne, germany', 7378.093972006955), ('dublin, ireland', 6880.455547319905)], 'san fransciso, ca, usa': [('new york, ny, usa', 4131.2260736717235), ('portland, me, usa', 4373.47227136685), ('seattle, wa, usa', 1096.7574923438176), ('denver, co, usa', 1525.0127604501427), ('austin, tx, usa', 2413.848547208374), ('montreal, qc, ca', 4086.2057553148447), ('vancouver, bc, ca', 1277.8536425787968), ('ancorage, ak, usa', 3227.9327095873814), ('toronto, on, ca', 3645.110544523325), ('berlin, germany', 9108.707449785496), ('frankfurt, germany', 9137.255927375238), ('hamburg, germany', 8884.368688361468), ('cologne, germany', 8990.289091553695), ('dublin, ireland', 8180.339239597275)], 'seattle, wa, usa': [('new york, ny, usa', 3867.6996796026383), ('portland, me, usa', 3998.364023733441), ('san fransciso, ca, usa', 1096.7574923438176), ('denver, co, usa', 1643.1750113542994), ('austin, tx, usa', 2850.7367348453713), ('montreal, qc, ca', 3676.5204899041464), ('vancouver, bc, ca', 187.98357684266733), ('ancorage, ak, usa', 2306.7618804760577), ('toronto, on, ca', 3327.3059742840283), ('berlin, germany', 8118.959756087951), ('frankfurt, germany', 8181.7466480361), ('hamburg, germany', 7904.721664019446), ('cologne, germany', 8038.878710196924), ('dublin, ireland', 7278.426972799073)], 'vancouver, bc, ca': [('new york, ny, usa', 3903.113264915231), ('portland, me, usa', 4012.0439582695217), ('seattle, wa, usa', 187.98357684266733), ('san fransciso, ca, usa', 1277.8536425787968), ('denver, co, usa', 1775.1965629670804), ('austin, tx, usa', 2997.5356232275794), ('montreal, qc, ca', 3686.260543142142), ('ancorage, ak, usa', 2134.1966806947084), ('toronto, on, ca', 3358.0392491366733), ('berlin, germany', 7981.580607175459), ('frankfurt, germany', 8053.506529112649), ('hamburg, germany', 7770.358385120253), ('cologne, germany', 7911.885681703947), ('dublin, ireland', 7165.131299170149)], 'new york, ny, usa': [('portland, me, usa', 447.6466430279615), ('seattle, wa, usa', 3867.6996796026383), ('san fransciso, ca, usa', 4131.2260736717235), ('denver, co, usa', 2620.6948689079463), ('austin, tx, usa', 2434.3591976539533), ('montreal, qc, ca', 531.2020828945784), ('vancouver, bc, ca', 3903.113264915231), ('ancorage, ak, usa', 5410.1446563811905), ('toronto, on, ca', 549.7598018862785), ('berlin, germany', 6383.77962681138), ('frankfurt, germany', 6201.852040536504), ('hamburg, germany', 6129.122382053943), ('cologne, germany', 6053.3583286022), ('dublin, ireland', 5114.01265148199)], 'dublin, ireland': [('new york, ny, usa', 5114.01265148199), ('portland, me, usa', 4671.044334218768), ('seattle, wa, usa', 7278.426972799073), ('san fransciso, ca, usa', 8180.339239597275), ('denver, co, usa', 7084.24910627883), ('austin, tx, usa', 7450.285692633587), ('montreal, qc, ca', 4761.172873284123), ('vancouver, bc, ca', 7165.131299170149), ('ancorage, ak, usa', 6880.455547319905), ('toronto, on, ca', 5252.688701589071), ('berlin, germany', 1316.7887794012324), ('frankfurt, germany', 1087.8776732761144), ('hamburg, germany', 1074.326634025533), ('cologne, germany', 939.6336729145872)], 'hamburg, germany': [('new york, ny, usa', 6129.122382053943), ('portland, me, usa', 5682.561366723863), ('seattle, wa, usa', 7904.721664019446), ('san fransciso, ca, usa', 8884.368688361468), ('denver, co, usa', 7926.157053263219), ('austin, tx, usa', 8406.075329700989), ('montreal, qc, ca', 5744.95620629242), ('vancouver, bc, ca', 7770.358385120253), ('ancorage, ak, usa', 7133.384628965154), ('toronto, on, ca', 6223.643112265524), ('berlin, germany', 254.80553039175498), ('frankfurt, germany', 392.87849479935215), ('cologne, germany', 356.6633710260719), ('dublin, ireland', 1074.326634025533)], 'toronto, on, ca': [('new york, ny, usa', 549.7598018862785), ('portland, me, usa', 734.3534731575957), ('seattle, wa, usa', 3327.3059742840283), ('san fransciso, ca, usa', 3645.110544523325), ('denver, co, usa', 2161.5284654374723), ('austin, tx, usa', 2199.140387071369), ('montreal, qc, ca', 505.05332964971956), ('vancouver, bc, ca', 3358.0392491366733), ('ancorage, ak, usa', 4877.203369569424), ('berlin, germany', 6477.866190491199), ('frankfurt, germany', 6335.256241201341), ('hamburg, germany', 6223.643112265524), ('cologne, germany', 6184.024494367389), ('dublin, ireland', 5252.688701589071)], 'denver, co, usa': [('new york, ny, usa', 2620.6948689079463), ('portland, me, usa', 2895.861939844122), ('seattle, wa, usa', 1643.1750113542994), ('san fransciso, ca, usa', 1525.0127604501427), ('austin, tx, usa', 1242.3361492683046), ('montreal, qc, ca', 2632.54876647224), ('vancouver, bc, ca', 1775.1965629670804), ('ancorage, ak, usa', 3854.9535418964065), ('toronto, on, ca', 2161.5284654374723), ('berlin, germany', 8169.4908092679), ('frankfurt, germany', 8118.555563804856), ('hamburg, germany', 7926.157053263219), ('cologne, germany', 7966.660997681989), ('dublin, ireland', 7084.24910627883)], 'austin, tx, usa': [('new york, ny, usa', 2434.3591976539533), ('portland, me, usa', 2840.9300348922857), ('seattle, wa, usa', 2850.7367348453713), ('san fransciso, ca, usa', 2413.848547208374), ('denver, co, usa', 1242.3361492683046), ('montreal, qc, ca', 2697.4254785041767), ('vancouver, bc, ca', 2997.5356232275794), ('ancorage, ak, usa', 5096.370282889052), ('toronto, on, ca', 2199.140387071369), ('berlin, germany', 8659.20097853115), ('frankfurt, germany', 8530.618772237909), ('hamburg, germany', 8406.075329700989), ('cologne, germany', 8378.98371782052), ('dublin, ireland', 7450.285692633587)], 'berlin, germany': [('new york, ny, usa', 6383.77962681138), ('portland, me, usa', 5937.266858217603), ('seattle, wa, usa', 8118.959756087951), ('san fransciso, ca, usa', 9108.707449785496), ('denver, co, usa', 8169.4908092679), ('austin, tx, usa', 8659.20097853115), ('montreal, qc, ca', 5999.59094399283), ('vancouver, bc, ca', 7981.580607175459), ('ancorage, ak, usa', 7284.160939006664), ('toronto, on, ca', 6477.866190491199), ('frankfurt, germany', 423.6316360409979), ('hamburg, germany', 254.80553039175498), ('cologne, germany', 477.43909710717696), ('dublin, ireland', 1316.7887794012324)]}