list index out of range in __init__() - python

I am not able to get that what's wrong with the below code it should show me the output as
0 : [1, 4]
1 : [0, 2, 4, 3]
2 : [1, 3]
3 : [2, 4, 1]
4 : [3, 0, 1]
But it showing error as below :
Traceback (most recent call last):
File "C:\Users\sanja\PycharmProjects\DSA\pythonProject\main.py", line 19, in <module>
g1 = Graph(num_nodes,edges)
File "C:\Users\sanja\PycharmProjects\DSA\pythonProject\main.py", line 10, in __init__
self.data[v1].append(v2)
IndexError: list index out of range
And the program look like below :
edges = list(tuple(map(int, input("enter edges: ").split())) for r in range(int(input("enter no of nodes: "))))
num_nodes =len(edges)
class Graph:
def __init__(self, num_nodes, edges):
self.data = [[] for _ in range(num_nodes)]
for v1, v2 in edges:
self.data[v1].append(v2)
self.data[v2].append(v1)
def __repr__(self):
return "\n".join(["{} : {}".format(i, neighbors) for (i, neighbors) in enumerate(self.data)])
def __str__(self):
return repr(self)
g1 = Graph(num_nodes,edges)
print(g1)
Here in the above program i am taking num of nodes as well as edges an showing the proper format as shown above in the 1st section but getting error.
The program below is doing same work that i need to do the only thing is i need to implement with user input means at run time the inputs to be given .
num_nodes1 = 5
edges1 = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (1, 4), (1, 3)]
num_nodes1, len(edges1)
class Graph:
def __init__(self, num_nodes, edges):
self.data = [[] for _ in range(num_nodes)]
for v1, v2 in edges:
self.data[v1].append(v2)
self.data[v2].append(v1)
def __repr__(self):
return "\n".join(["{} : {}".format(i, neighbors) for (i, neighbors) in enumerate(self.data)])
def __str__(self):
return repr(self)
g1 = Graph(num_nodes1, edges1)
print(g1)

Issues: There are a couple of issues in the code.
The edges specified can be more than the number of nodes so you will need to know how many edges to read in (e.g. 7 edges for 5 nodes)
You need unique edges in self.data for each node. You will need to remove duplicates.
Solution: Please find the code for the __init__ function with the corrections below.
def __init__(self, num_nodes, edges):
self.data = [[] for _ in range(num_nodes)]
# link nodes
for v1, v2 in edges:
for edge in edges:
self.data[v1].append(v2)
self.data[v2].append(v1)
# remove duplicates
self.data = [list(set(entry)) for entry in self.data]
You would invoke the above function as follows:
num_nodes = int(input("enter no of nodes: "))
num_edges = int(input("how many edges?: "))
edges = [tuple(map(int, input("enter edge: ").split())) for r in range(num_edges)]
g1 = Graph(num_nodes, edges)
print(g1)
Output: Running the above with your sample data will produce the following when you run print(self.data):
0 : [1, 4]
1 : [0, 2, 3, 4]
2 : [1, 3]
3 : [1, 2, 4]
4 : [0, 1, 3]

Related

Monotonic Shortest Path of a directed graph in python

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

Trying to implement numbers from text file, into adjacency list project in python

I am trying to take data from a text file and implement it into this adjacency code below.
This is the text file (test.txt):
1,1
1,1
4,1
4,5
5,1
5,3
1,1
3,3
Using this code I was able to break down the text file int a list:
data = open("test.txt", "r")
list_of_lists = []
for line in data.readlines():
stripped_line = line.strip('\n')
line_list = list(map(int, stripped_line.split(',')))
list_of_lists.append(line_list)
data.close()print(list_of_lists)
adjLists = list_of_lists
return adjList
The output from the code above is:
[[1, 1], [1, 1], [4, 1], [4, 5], [5, 1], [5, 3], [1, 1], [3, 3]]
What I cannot figure out, is how to take the data from the list, and implement it into the code below so it creates it into an adjacency list it runs the text file numbers.
def __init__(self, nodes : int) :
# Store the adjacency list as a dictionary
# { 0 : [ 1, 2 ], 1 : [ 3, 4 ] }
# The default dictionary would create an empty list as a default (value)
# for the nonexistent keys.
self.adjlist = defaultdict(list)
self.nodes = nodes
def AddEdge (self, src : int, dst : int) :
self.adjlist[src].append(dst)
self.adjlist[dst].append(src)
def Display_AdjList(self) :
for item in self.adjlist.items() :
print (item)
def main():
nodes = 7
g = Graph(nodes)
g.AddEdge(0, 1)
g.AddEdge(0, 2)
g.AddEdge(1, 3)
g.AddEdge(1, 4)
g.AddEdge(2, 3)
g.AddEdge(3, 5)
g.AddEdge(4, 6)
g.AddEdge(5, 6)
print("Adjacency list for storing graph")
g.Display_AdjList()
if __name__ == "__main__" :
main()
Each list of two items in your list is one edge in the adjacency matrix. You need to create a graph, then loop through each of the edges and add them to the graph:
g = Graph(nodes)
for edge in adjList:
g.AddEdge(*edge)
g.Display_AdjList()

Accessing items in a list, and forming graphs

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.

Finding a pair of elements in a list that adds up to a sum using a dictionary

Context of the problem:
Find Pair with given Sum in the Array.
Given an unsorted list of ints, find a pair with a given sum in it.
EXAMPLE:
list = [8, 7, 2, 5, 3, 1]
sum = 10
OUTPUT = index 0 & 2 (8, 2) or 1 & 4 (7, 3)
This is what I have so far:
def find_pair_dict(ints: [int], sum_: int):
dict_ = dict()
# {
# element: index
# 8: 0,
# 7: 1,
# ...
# }
output = list()
for i in range(len(ints)):
diff = sum_ - ints[i]
# print(diff)
if diff not in dict_.keys():
# int: index
dict_[ints[i]] = i
else:
output.append((dict_[ints[i]], dict_[diff]))
if not output:
return "No pairs were found"
return output
I am calling this function with find_pair_dict([8, 7, 2, 5, 3, 1], 10) and am getting an error that I do not understand.
The Error
Traceback (most recent call last):
File "find_pair_sum.py", line 62, in <module>
print(find_pair_dict([8, 7, 2, 5, 3, 1], 10))
File "find_pair_sum.py", line 53, in find_pair_dict
output.append((dict_[ints[i]], dict_[diff]))
KeyError: 2
This sounds like the element of 2 cannot be added?
Almost a one-liner:
def find_pairs(ints: [int], sum_: int):
return {
tuple(sorted((n, ints.index(sum_-i)))): (i, sum_-i)
for n, i in enumerate(ints) if sum_ - i in ints
}
print(find_pairs([8, 7, 2, 5, 3, 1], 10))
Result:
{(0, 2): (2, 8), (1, 4): (3, 7), (3, 3): (5, 5)}
Note: the key to the dictionary is a sorted tuple. A tuple because a list isn't hashable and sorted to avoid both (0,2) and (2,0) showing up as keys (for example).
The error in your solution occurs because the first time this line is executed:
output.append((dict_[ints[i]], dict_[diff]))
The value if dict_ is {8: 0, 7: 1} and ints[i] is 2. Since there is no entry for 2 in the dict, you get this error.
Actually I think there should be three outputs:
(2,0) = 2,8
(4,1) = 3,7
(3,3) = 5,5
The problem has already been succinctly described by #Selcuk. I am suggesting you how to solve that. Many ways. Easiest for me is to use defaultdict from collections.
Below is the code with minimal changes
from collections import defaultdict
def find_pair_dict(ints: [int], sum_: int):
dict_ = defaultdict(list)
output = list()
for i in range(len(ints)):
diff = sum_ - ints[i]
dict_[ints[i]] = i
output.append((dict_[ints[i]], dict_[diff]))
if not output:
return "No pairs were found"
return output
print(find_pair_dict([8, 7, 2, 5, 3, 1], 10))
This prints out the following. I have not filtered for empty matches.
[(0, []), (1, []), (2, 0), (3, 3), (4, 1), (5, [])]
Is this what you wanted?

Topological sort python

I coded a solution for DFS non-recursive, but i can't modify it to make a topological sort:
def dfs(graph,start):
path = []
stack = [start]
while stack != []:
v = stack.pop()
if v not in path: path.append(v)
for w in reversed(graph[v]):
if w not in path and not w in stack:
stack.append(w)
return path
Any ideas how to modify it?
With the recursive version i can easy have the sorting:
def dfs_rec(graph,start,path):
path = path + [start]
for edge in graph[start]:
if edge not in path:
path = dfs_rec(graph, edge,path)
print start
return path
Input:
>>> graph = {
1: [2, 3],
2: [4, 5, 6],
3: [4,6],
4: [5,6],
5: [6],
6: []
}
>>> dfs_rec(graph,1,[])
6
5
4
2
3
1
[1, 2, 4, 5, 6, 3]
>>> dfs(graph,1)
[1, 2, 4, 5, 6, 3]
>>> graph = {
1: [3],
3: [5,6],
5: [4],
4: [7],
7: [],
6: []
}
>>> print dfs_rec(graph,1,[])
7
4
5
6
3
1
[1, 3, 5, 4, 7, 6]
>>> print dfs(graph,1)
[1, 3, 5, 4, 7, 6]
so i need to get this ordering in the non-recursive also.
Non-recursive solution:
I think that this also could be the solution, mark me if i am wrong.
def dfs(graph,start):
path = []
stack = [start]
label = len(graph)
result = {}
while stack != []:
#this for loop could be done in other ways also
for element in stack:
if element not in result:
result[element] = label
label = label - 1
v = stack.pop()
if v not in path: path.append(v)
for w in reversed(graph[v]):
if w not in path and not w in stack:
stack.append(w)
result = {v:k for k, v in result.items()}
return path,result
Input:
graph = { 1: [3], 3:[5,6] , 5:[4] , 4:[7], 7:[],6:[]}
print dfs(graph,1)
Output:
([1, 3, 5, 4, 7, 6], {1: 7, 2: 4, 3: 5, 4: 6, 5: 3, 6: 1})
1
/
3
/\
5 6
/
4
/
7
FWIW, here is some code I worked up for a non-recursive topological sort.
from collections import defaultdict, namedtuple
from itertools import islice
Results = namedtuple('Results', ['sorted', 'cyclic'])
def topological_sort(dependency_pairs):
'Sort values subject to dependency constraints'
num_heads = defaultdict(int) # num arrows pointing in
tails = defaultdict(list) # list of arrows going out
heads = [] # unique list of heads in order first seen
for h, t in dependency_pairs:
num_heads[t] += 1
if h in tails:
tails[h].append(t)
else:
tails[h] = [t]
heads.append(h)
ordered = [h for h in heads if h not in num_heads]
for h in ordered:
for t in tails[h]:
num_heads[t] -= 1
if not num_heads[t]:
ordered.append(t)
cyclic = [n for n, heads in num_heads.items() if heads]
return Results(ordered, cyclic)
if __name__ == '__main__':
print( topological_sort('aa'.split()) )
print( topological_sort('ah bg cf ch di ed fb fg hd he ib'.split()) )
from collections import defaultdict, deque
class Graph:
def __init__(self, directed=False, nodes=None, edges=None):
self.graph = defaultdict(list)
self.directed = directed
self.add_nodes(nodes)
self.add_edges(edges)
#property
def nodes(self):
if not self.directed:
return list(self.graph.keys())
elif self.directed:
nodes = set()
nodes.update(self.graph.keys())
for node in self.graph.keys():
for neighbor in self.graph[node]:
nodes.add(neighbor)
return list(nodes)
def add_node(self, node):
if node not in self.nodes:
self.graph[node] = list()
def add_nodes(self, nodes):
if nodes is None:
return None
for node in nodes:
self.add_node(node)
#property
def edges(self):
edges = list()
for source, neighbors in self.graph.items():
for neighbor in neighbors:
edges.append((source, neighbor))
return edges
def add_edge(self, edge):
node1, node2 = edge
self.graph[node1].append(node2)
if not self.directed:
self.graph[node2].append(node1)
def add_edges(self, edges):
if edges is None:
return None
for edge in edges:
self.add_edge(edge)
def topological_util(self, node, visited, label):
visited[node] = True
for edge in self.graph[node]:
if not visited[edge]:
self.topological_util(edge, visited, label)
label.appendleft(node)
def topological_sort(self):
visited = dict.fromkeys(self.nodes, False)
# store all nodes in topological order, the index is the position
label = deque()
for node in self.nodes:
if not visited[node]:
self.topological_util(node, visited, label)
return label
#this algorithm gives the logic of topological sorting..if u want to run this
#give adjacency mat of your choice and this algorithm works on graph elements ranging from 0 to n
a=[[0,0,1,0,0,0],[0,0,1,0,0,0],[0,0,0,1,1,0],[0,0,0,0,1,0],[0,0,0,0,0,0],[0,0,1,0,0,0]]
vis=[0 for i in range(0,len(a))]
s=[]
orderstack=[]#stores the reverse order of topological sorted elements
def dfs_for_topological_sorting(a,vis,i):
vis[i]=1
x=0
for j in range(0,len(a[0])):
if(a[i][j]==1 and vis[j]==0):
x=1
s.append(j)
#print(s)
dfs_for_topological_sorting(a,vis,j)
if(x==0 and len(s)!=0):
orderstack.append(s[len(s)-1])
if(len(s)>0):
dfs_for_topological_sorting(a,vis,s.pop())
for i in range(0,len(a)):
if(i not in orderstack):
s.append(i)
dfs_for_topological_sorting(a,vis,i)
print(orderstack[len(orderstack)-1::-1])
from collections import defaultdict # importing defaultdict
def topological_sort(graph,b,a): # defining function
T = []
visited = []
in_degree = []
for i in range(a+1):
in_degree.append(0) # initialising the degree of each vertex =0
visited.append(0) # initialising all the vertics unvisited
for i in range(1,a+1):
for j in graph[i]:
in_degree[j] = in_degree[j] + 1 # now assigning and incrementing
Queue=[] # the degree of each vertex acc.
for i in range(1,a+1):
if in_degree[i]==0:
Queue.append(i) # appending those vertices which have zero
visited[i] = 1 # degree and making it as visited
while Queue :
vertex = Queue.pop(Queue.index(min(Queue))) # popping each element in
T.append(vertex) # lexicographical order and
for j in graph[vertex]: # appending to T
if visited[j]==0:
in_degree[j] = in_degree[j] - 1
if in_degree[j] == 0:
Queue.append(j) #according to each popped vertex
visited[j] = 1 #as key in graph check whether
return T #in list corresponding to key
# as value,is it not visited and
#decreasing its value when it
#becomes zero,append it to queue
#and mark it as visited
graph=defaultdict(list)
a,b=list(map(int,input().split())) #a=no. of vertices
for i in range(b): #b=no. of edges
p,q=list(map(int,input().split()))
graph[p].append(q) # we take input in graph as DAG
ss=topological_sort(graph,b,a) # calling function
for i in ss:
print(i,end=" ")
'''Input
5 6
1 2
1 3
2 3
2 4
3 4
3 5
Your Code's Output
1 2 3 4 5
Expected Correct Output
1 2 3 4 5 '''

Categories