I'm currently trying to make an algorithm that gives me the total cost of a graph when all nodes have been visited but am failing miserably and honestly out of ideas. My goal is to get the total costs of the graphs below, using the Dijkstra algorithm.
Here's what I have so far:
from collections import defaultdict
import heapq
def build_graph():
# Create the graph with the all given nodes and costs
edges = aeroDistances
graph = defaultdict(dict)
for edge in edges.items():
tuple1 = edge[0][0]
tuple2 = edge[0][1]
cost = edge[1]
connection1 = {tuple2 : cost}
connection2 = {tuple1 : cost}
graph[tuple1].update(connection1)
graph[tuple2].update(connection2)
return dict(graph)
def dijkstra(graph, starting_vertex):
# All the distances set to infinity
distances = {vertex: float('infinity') for vertex in graph}
# Distance from the starting vertex
distances[starting_vertex] = 0
# Priority queue
pq = [(0, starting_vertex)]
while len(pq) > 0:
current_distance, current_vertex = heapq.heappop(pq)
# Nodes can get added to the priority queue multiple times. We only
# process a vertex the first time we remove it from the priority queue
if current_distance > distances[current_vertex]:
continue
for neighbor, weight in graph[current_vertex].items():
distance = current_distance + weight
# Only consider this new path if it's better than any path we've
# already found
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(pq, (distance, neighbor))
return distances, distance
numCidades = 0
numAeroportos = 0
numEstradas = 0
#custoAeroportos = {}
#custoEstradas = {}
#custoAeroportos = {(1, 2): 2, (1, 3): 4}
#custoEstradas = {(3, 1, 'E'): 2}
#custoAeroportos = {(1, 2): 1, (2, 3): 2, (3, 4): 1, (4, 1): 1}
custoAeroportos = {(1, 2): 1, (1, 3): 6, (2, 4): 2, (3, 4): 2}
custoEstradas = {(2, 3): 3}
listCidades = [1,2,3]
distances = []
indexValue = 0
indexKey = 0
currentIndex = 0
# Deconstruct the dict into a list of keys (tuples)
# Deconstruct the dict into a list of values
# Make it easier to sort the connections by creating a list of tuples and
# their respective weights and zip them toghether
distancesAeroKeys = list(custoAeroportos.keys())
distancesAeroValues = list(custoAeroportos.values())
aeroDistances = dict(map(list, zip(*[distancesAeroKeys, distancesAeroValues])))
print()
print("AeroDistances: " + str(aeroDistances))
graph = build_graph()
print()
print("Graph: " + str(graph))
print()
print("Dijkstra: " + str(dijkstra(graph, 1)))
The two graphs, dicts, I'm currently trying this with are named custoAeroportos and I can't seem to get the total minimum cost when all nodes are visited.
Here're the graphs, they are fairly simple:
This one has a total cost of 5
This one has a total cost of 3
The total cost I'm getting is wrong and I can't figure it out.
For the first graph:
AeroDistances: {(1, 2): 1, (1, 3): 6, (2, 4): 2, (3, 4): 2}
Graph: {1: {2: 1, 3: 6}, 2: {1: 1, 4: 2}, 3: {1: 6, 4: 2}, 4: {2: 2, 3: 2}}
Dijkstra: ({1: 0, 2: 1, 3: 5, 4: 3}, 7)
For the second graph, which somehow is correct:
AeroDistances: {(1, 2): 1, (2, 3): 2, (3, 4): 1, (4, 1): 1}
Graph: {1: {2: 1, 4: 1}, 2: {1: 1, 3: 2}, 3: {2: 2, 4: 1}, 4: {3: 1, 1: 1}}
Dijkstra: ({1: 0, 2: 1, 3: 2, 4: 1}, 3)
I really appreciate your help, thank you.
Your function returns the distance of the path from the starting vertex to whichever was the last node that was added to the heap. This is not really what you want to return. Certainly when the BFS-tree has multiple outgoing edges from some vertices, this path has little to do with the total distance.
Instead you need to accumulate the weights of the edges that are "accepted", i.e. those that are (implicitly) popped from the heap and improve the distance for that node.
So I would suggest extending the tuples on the heap with one more information: the weight of the last edge that brought us to that node. When the node is accepted, then this edge becomes part of the spanning tree, and its weight should then be added to an accumulating total.
Here is the adapted code. The changes have accompanying comments:
def dijkstra(graph, starting_vertex):
distances = {vertex: float('infinity') for vertex in graph}
distances[starting_vertex] = 0
graph_distance = 0 # this will be returned
pq = [(0, 0, starting_vertex)] # middle value is edge weight
while len(pq) > 0:
current_distance, edge_weight, current_vertex = heapq.heappop(pq)
if current_distance > distances[current_vertex]:
continue
graph_distance += edge_weight # accumulate
for neighbor, weight in graph[current_vertex].items():
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(pq, (distance, weight, neighbor)) # include weight
return distances, graph_distance # ...return it
Related
I am trying to get the following code working. After every for ends heappop gives me an integer instead of Vertex. In addition when I got it working, with changing the Vertex in the priority queue with integer. I have wrong result. Please help.
Thanks in advance
import heapq
class Vertex:
def __init__(self, id):
self.id = id
self.adjList = []
self.adjWeights = []
def shortestPath(vertices, N, source, destination):
distTo = [float('inf') for _ in range(N+1)]
edgeTo = [float('inf') for _ in range(N+1)]
# Set initial distance from source
# to the highest value
distTo[source] = 0.0
edgeTo[source] = float('inf')
pq = [vertices[source]]
heapq.heapify(pq)
while True:
closest = heapq.heappop(pq)
for i in range(len(closest.adjList)):
# Checks if the edges are decreasing and
# whether the current directed edge will
# create a shorter path
if closest.adjWeights[i] < edgeTo[closest.id] and distTo[closest.id] + closest.adjWeights[i] < distTo[closest.adjList[i]]:
edgeTo[closest.adjList[i]] = closest.adjWeights[i]
distTo[closest.adjList[i]] = closest.adjWeights[i] + distTo[closest.id];
heapq.heappush(pq, closest.adjList[i])
print(distTo)
print(distTo[destination])
def main ()
N = 6
M = 9
'''
edges = {{0, 2, 1.1}, {0, 4, 2}, {0, 5, 3.3}, {1, 4, 2.7},
{2, 3, 2}, {2, 4, 1.1}, {3, 1, 2.3}, {4, 5, 2.4}, {5, 1, 3}}
'''
# Create an array of vertices
vertices = [Vertex(i) for i in range(0, N)]
i=0
vertices[0].adjList.append(2)
vertices[0].adjWeights.append(1.1)
vertices[0].adjList.append(4)
vertices[0].adjWeights.append(2.0)
vertices[0].adjList.append(5)
vertices[0].adjWeights.append(3.3)
vertices[1].adjList.append(4)
vertices[1].adjWeights.append(2.7)
vertices[2].adjList.append(3)
vertices[2].adjWeights.append(2.0)
vertices[2].adjList.append(4)
vertices[2].adjWeights.append(1.1)
vertices[3].adjList.append(1)
vertices[3].adjWeights.append(2.3)
vertices[4].adjList.append(5)
vertices[4].adjWeights.append(2.4)
vertices[5].adjList.append(1)
vertices[5].adjWeights.append(3.0)
# Source and destination vertices
src = 0
target = 1
print(shortestPath(vertices, N, src, target))
As per title, given n sets like the following:
set1 = {"aa","ab","ba","cc","ca"},
set2 = {"fr","fa","po","pl","cc"},
set3 = {"fr","xz","hn","fa"},
set4 = {"jq","we","hn","ca","aa","fk"},
set5 = {"jp","wx","we","fr","ba"}
I want to get the exclusively intersections between them, conceptually like a Venn Diagram representation. So, for example:
The intersection between the set2 and set3, despite sharing {"fr","fa"}, will only be {"fa"}, as the string "fr" is also present in the intersection between set2,set3 and set5.
The brilliant solution proposed in this answer managed correctly all the permutations of given sets, but didn't manage to account for exclusivity, considering each overlap independently. So, in the previous example, it'll return {"fr","fa"}
Tryout
I tried to handle this situation in a very slow way, and want to know if there's a more proficient way to do the job.
By creating a list of sets
set_list = [set1, set2, set3, set4, set5]
I start from finding the whole sets intersection
whole_intersect = set.intersections(*map(set,set_list))
Then I'm moving in every permutation of length n-1 using list indices
isect_1_2_3_4 = set.intersections(*map(set, map(set_list.__getitem__, [0,1,2,3])) - whole_intersect
isect_1_2_3_5 = set.intersections(*map(set, map(set_list.__getitem__, [0,1,2,5])) - whole_intersect
..
Then by n-2
isect_1_2_3 = set.intersections(*map(set, map(set_list.__getitem__, [0,1,2])) - whole_intersect - isect_1_2_3_4 - isect_1_2_3_5
..
Till filling intersections of 2 sets
isect_1_2 = set.intersections(*map(set, map(set_list.__getitem__, [0,1,2])) - whole_intersect - isect_1_2_3_4 - isect_1_2_3_5 - isect_1_2_3 - isect_1_2_4 - isect_1_2_5
..
As expectable, this approach is a true pain. How could I manage to do it in a less handcrafted and pythonic way?
I think I have done it by first making a dictionary of all of the intersections, of every combination, and then finding the unique intersection by taking the intersection and subtracting the union of every other intersection:
import itertools
set1 = {"aa","ab","ba","cc","ca"}
set2 = {"fr","fa","po","pl","cc"}
set3 = {"fr","xz","hn","fa"}
set4 = {"jq","we","hn","ca","aa","fk"}
set5 = {"jp","wx","we","fr","ba"}
sets = {
1: set1,
2: set2,
3: set3,
4: set4,
5: set5
}
intersections = {}
for n_combinations in range(2, len(sets) + 1):
tmp = list(map(dict, itertools.combinations(sets.items(), n_combinations)))
tmp = {tuple(x.keys()):set.intersection(*list(x.values())) for x in tmp}
intersections.update(tmp)
unique_in_intersection = {}
for n_combinations in range(2, len(sets)+1):
for lookup_set in itertools.combinations(range(1, len(sets)+1), n_combinations):
s1_intersection_s2 = intersections[lookup_set]
union_other_intersections = set.union(*[v for k, v in intersections.items() if k != lookup_set and len(k) > len(lookup_set)])
unique_in_intersection[lookup_set] = s1_intersection_s2 - union_other_intersections
result:
{
(1, 2): {'cc'},
(1, 3): set(),
(1, 4): {'ca', 'aa'},
(1, 5): {'ba'},
(2, 3): {'fa'},
(2, 4): set(),
(2, 5): set(),
(3, 4): {'hn'},
(3, 5): set(),
(4, 5): {'we'},
(1, 2, 3): set(),
(1, 2, 4): set(),
(1, 2, 5): set(),
(1, 3, 4): set(),
(1, 3, 5): set(),
(1, 4, 5): set(),
(2, 3, 4): set(),
(2, 3, 5): {'fr'},
(2, 4, 5): set(),
(3, 4, 5): set(),
(1, 2, 3, 4): set(),
(1, 2, 3, 5): set(),
(1, 2, 4, 5): set(),
(1, 3, 4, 5): set(),
(2, 3, 4, 5): set(),
(1, 2, 3, 4, 5): set()
}
I want to convert a map to a graph/adjacent list/adjacent matrix.
For instance, suppose I have a map with # mark represents inaccessible walls and number represents nodes:
#######
#12 5#
###3###
#4 #
#######
The node coordinates:
1: (1, 3)
2: (2, 3)
3: (3, 2)
4: (1, 1)
5: (5, 3)
The output of this problem should be a graph like this:
1-2-5
\ /
3
|
4
1 can only connect to 2, as 2 is the only node that 1 can reach without passing other node. 2 can connect to 1, 3 and 5, the same reason as above.
The walls and nodes coordinates are given. I want an algorithm to convert any given map to a graph like the one above. Can anyone come up with a solution?
This is another solution, that works even if some nodes are not connected at all with the others, and a bit less memory costly :
def getNodes(l):
a={}
for i in range(len(l)):
for j in range(len(l[i])):
if (type(l[i][j]) is type(1)):
a.update({l[i][j]:(i,j)})
return a
def searchAdjascentNodes(m,coordonates):
nodes=set()
old=set()
toDo=set([coordonates])
while (len(toDo)!=0):
discoveredVertices=[]
for i in toDo:
adjascent=[j for j in [(i[0],i[1]+1), (i[0],i[1]-1), (i[0]+1,i[1]), (i[0]-1,i[1])] if (j[0]>=0 and j[0]<len(m) and j[1]>=0 and j[1]<len(m[0]) and j not in old and j not in toDo and j not in discoveredVertices)]
for a in adjascent:
if(type(m[a[0]][a[1]]) is int):
nodes.add(m[a[0]][a[1]])
elif(m[a[0]][a[1]]==' '):
discoveredVertices.append(a)
old=toDo.copy()
toDo=set(discoveredVertices)
return nodes
def createGraph(m):
n=getNodes(m)
r={}
for i in n:
r.update({i:searchAdjascentNodes(m,n[i])})
return r
You can run it like this :
theMap=[['#','#','#','#','#','#','#'],['#',1,2,' ',' ',5,'#'],['#','#','#',3,'#','#','#'],['#',4,' ',' ',' ',' ','#'],['#','#','#','#','#','#','#']]
print(createGraph(theMap))
And you get :
{1: {2}, 2: {1, 3, 5}, 5: {2, 3}, 3: {2, 4, 5}, 4: {3}}
You can use recursion to find the connections between the digits in the map, even if they are not directly adjacent to each other:
from functools import reduce as rd
s = """
#######
#12 5#
###3###
#4 #
#######
"""
#convert the map to a coordinate dictionary
coords = {(k, j):b for j, a in enumerate(filter(None, s.split('\n'))) for k, b in enumerate(a)}
def to_graph(start, seen = []):
t_coords = [lambda x, y:(x+1, y), lambda x, y:(x+1, y+1), lambda x, y:(x, y+1), lambda x, y:(x-1, y), lambda x, y:(x-1, y-1), lambda x, y:(x, y-1)]
#find possible valid squares to consume
if (ops:=[k for f in t_coords if (k:=(f(*start))) in coords and coords[k] != '#']):
#recursively check possible squares
results = [to_graph(i, seen+[start]) for i in ops if i not in seen]
vals = rd(lambda x, y:{**x, **y}, results, {}) #merge results
return {coords[start]:vals} if coords[start].isdigit() else vals
print(to_graph((1, 1))) #start with coordinates of any value
Output:
{'1': {'2': {'5': {}, '3': {'4': {}, '5': {}}}}}
The output returns a dictionary showing the relationships between the values as positioned in the map: 1 -> 2, 2 -> (5, 3), 3 -> (4, 5)
I have a list of 2D numpy arrays:
linelist = [[[0,0],[1,0]],[[0,0],[0,1]],[[1,0],[1,1]],[[0,1],[1,1]],[[1,2],[3,1]],[[1,2],[2,2]],[[3,1],[3,2]],[[2,2],[3,2]]]
Each line in linelist is the array of vertices connecting the edge.
These elements are the lines that form two squares:
-----
| |
-----
-----
| |
-----
I want to form two graphs, one for each square. To do this, I use a for loop. If neither vertex is present in the graph, then we form a new graph. If one vertex is present in the linelist, then it gets added to a present graph. In order for two lines to be connected, they need to share a vertex in common. However, I am having trouble coding this.
This is what I have so far:
graphs = [[]]
i=0
for elements in linelist:
for graph in graphs:
if elements[0] not in graph[i] and elements[1] not in graph[i]:
graphs.append([])
graphs[i].append(elements)
i=i+1
else:
graphs[i].append(elements)
I suggest doing a 'diffusion-like' process over the graph to find the disjoint subgraphs. One algorithm that comes to mind is breadth-first search; it works by looking for what nodes can be reached from a start node.
linelist = [[[0,0],[1,0]],[[0,0],[0,1]],[[1,0],[1,1]],[[0,1],[1,1]],[[1,2],[3,1]],[[1,2],[2,2]],[[3,1],[3,2]],[[2,2],[3,2]]]
# edge list usually reads v1 -> v2
graph = {}
# however these are lines so symmetry is assumed
for l in linelist:
v1, v2 = map(tuple, l)
graph[v1] = graph.get(v1, ()) + (v2,)
graph[v2] = graph.get(v2, ()) + (v1,)
def BFS(graph):
"""
Implement breadth-first search
"""
# get nodes
nodes = list(graph.keys())
graphs = []
# check all nodes
while nodes:
# initialize BFS
toCheck = [nodes[0]]
discovered = []
# run bfs
while toCheck:
startNode = toCheck.pop()
for neighbor in graph.get(startNode):
if neighbor not in discovered:
discovered.append(neighbor)
toCheck.append(neighbor)
nodes.remove(neighbor)
# add discovered graphs
graphs.append(discovered)
return graphs
print(BFS(graph))
for idx, graph in enumerate(BFS(graph)):
print(f"This is {idx} graph with nodes {graph}")
Output
This is 0 graph with nodes [(1, 0), (0, 1), (0, 0), (1, 1)]
This is 1 graph with nodes [(3, 1), (2, 2), (1, 2), (3, 2)]
You may be interested in the package networkx for analyzing graphs. For instance finding the disjoint subgraphs is pretty trivial:
import networkx as nx
tmp = [tuple(tuple(j) for j in i) for i in linelist]
graph = nx.Graph(tmp);
for idx, graph in enumerate(nx.connected_components(graph)):
print(idx, graph)
My approach involves 2 passes over the list. In the first pass, I will look at the vertices and assign a graph number to each (1, 2, ...) If both vertices have not been seen, I will assign a new graph number. Otherwise, assign it to an existing one.
In the second pass, I go through the list and group the edges that belong to the same graph number together. Here is the code:
import collections
import itertools
import pprint
linelist = [[[0,0],[1,0]],[[0,0],[0,1]],[[1,0],[1,1]],[[0,1],[1,1]],[[1,2],[3,1]],[[1,2],[2,2]],[[3,1],[3,2]],[[2,2],[3,2]]]
# First pass: Look at the vertices and figure out which graph they
# belong to
vertices = {}
graph_numbers = itertools.count(1)
for v1, v2 in linelist:
v1 = tuple(v1)
v2 = tuple(v2)
graph_number = vertices.get(v1) or vertices.get(v2) or next(graph_numbers)
vertices[v1] = graph_number
vertices[v2] = graph_number
print('Vertices:')
pprint.pprint(vertices)
# Second pass: Sort edges
graphs = collections.defaultdict(list)
for v1, v2 in linelist:
graph_number = vertices[tuple(v1)]
graphs[graph_number].append([v1, v2])
print('Graphs:')
pprint.pprint(graphs)
Output:
Vertices:
{(0, 0): 1,
(0, 1): 1,
(1, 0): 1,
(1, 1): 1,
(1, 2): 2,
(2, 2): 2,
(3, 1): 2,
(3, 2): 2}
Graphs:
defaultdict(<type 'list'>, {1: [[[0, 0], [1, 0]], [[0, 0], [0, 1]], [[1, 0], [1, 1]], [[0, 1], [1, 1]]], 2: [[[1, 2], [3, 1]], [[1, 2], [2, 2]], [[3, 1], [3, 2]], [[2, 2], [3, 2]]]})
Notes
I have to convert each vertex from a list to a tuple because list cannot be a dictionary's key.
graphs behaves like a dictionary, the keys are graph numbers (1, 2, ...) and the values are list of edges
A little explanation of the line
graph_number = vertices.get(v1) or vertices.get(v2) or next(graph_numbers)
That line is roughly equal to:
number1 = vertices.get(v1)
number2 = vertices.get(v2)
if number1 is None and number2 is None:
graph_number = next(graph_numbers)
elif number1 is not None:
graph_number = number1
else:
graph_number = number2
Which says: If both v1 and v2 are not in the vertices, generate a new number (i.e. next(graph_numbers)). Otherwise, assign graph_number to whichever value that is not None.
Not only that line is succinct, it takes advantage of Python's short circuit feature: The interpreter first evaluate vertices.get(v1). If this returns a number (1, 2, ...) then the interpreter will return that number and skips evaluating the vertices.get(v2) or next(graph_numbers) part.
If vertices.get(v1) returns None, which is False in Python, then the interpreter will evaluate the next segment of the or: vertices.get(v2). Again, if this returns a non-zero number, then the evaluation stops and that number is return. If vertices.get(v2) returns None, then the interpreter evaluates the last segment, next(graph_numbers) and returns that value.
I'm trying to implement Coin Changing Problem which states that
Coin Changing problem is described as given a value N, if we want to
make change for N cents, and we have infinite supply of each of S =
{S1, S2, . . . , Sm} valued coins, how many ways can we make the
change? The order of coins doesn’t matter. Identify all possible ways
of changing N cent by Greedy Method. For example, for N = 4 and S =
{1, 2, 3}, there are four solutions: {1, 1, 1, 1}, {1, 1, 2}, {2, 2},
{1, 3}. So output should be 4. For N = 10 and S = {2, 5, 3, 6}, there
are five solutions: {2, 2, 2, 2, 2}, {2, 2, 3, 3}, {2, 2, 6}, {2, 3,
5}and{5, 5}. So the output should be 5.
Note: This not the original Coin changing problem where we have to find the optimal solution (i.e. Minimum No. of coins)
In my below python implementation, I'm using a list name called $check$. Problem is that the program is using same $check$ list throughout its run time and hence I'm getting wrong result. It should use the $check$ list which is local to its function call. Can anybody find a way out of this..
# N
N = 10
# Set of Changes
s = [2, 3, 5, 6]
lst = []
check = [0, 0, 0, 0]
def Coin_Change_Count(C, check):
for k in range(len(s)):
i = len(s) - k - 1
t = C - s[i]
if (t >= 0):
if (s[i] == 2):
check[0] += 1
elif (s[i] == 3):
check[1] += 1
elif (s[i] == 5):
check[2] += 1
elif (s[i] == 6):
check[3] += 1
if (t >= s[0]):
Coin_Change_Count(t, check)
if (t == 0):
if (not (check in lst)):
lst.append(check)
Coin_Change_Count(N, check)
print(len(lst))