Skip to content

Instantly share code, notes, and snippets.

@rsnape
Created August 1, 2015 22:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rsnape/53bac7bbd979ae8ffba6 to your computer and use it in GitHub Desktop.
Save rsnape/53bac7bbd979ae8ffba6 to your computer and use it in GitHub Desktop.
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))
@tristanfisher
Copy link

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)]}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment