Hi all – I'm new to coding if anybody is wondering.
This is my first time posting here and I'm currently stuck on one of my assignments. The code below is my draft code.
The expected output for adjacency_list is:
[[1,3], [2], [3], [0,2]]
# with possibly [3,1] instead of [1,3] or [0,2] instead of [2,0]
The output from my code is:
[[1, 3], [2], [3], [0, 2]] – which is what I wanted
And for the expected output for maximal_path is:
[0, 1, 2, 3] and [0, 3, 2]
Whereas my output is:
[0, 1, 3, 2] – which is totally different from the expected output that my professor wanted. I have been going through the second function and keep hitting a dead end. Can anyone show where my mistake is with the second function?
Here is the digraph.txt list:
6 4
0 1
0 3
1 2
2 3
3 0
3 2
–
Thank you!
def adjacency_list(file_name):
# create empty list
adj_temp_list = []
# open input file
f = open('digraph0.txt','r')
# read first line
in_file = f.readline()
first_row = [int(i) for i in in_file.strip().split()]
# first_row[0] represents number of edges and first_row[1] represents number of vertices
# add number of vertices amount of empty lists to adj_temp_list
for i in range(first_row[1]):
adj_temp_list.append([])
# read edges( each line represents one edge)
for i in range(first_row[0]):
line = f.readline()
# split the line and convert the returned list into a list of integers
vertex=[int(i) for i in line.strip().split()]
# vertex[0] and vertex[1] represents start vertex and end vertex respectively.
# that is, vertex[1] is adjacent vertex or neighbour to vertex[0]
# Thus, append vertex[1] to the adjcency list of vertex[0]
adj_temp_list[vertex[0]].append(vertex[1])
# close input file
f.close()
# create a new empty list
adj_list=[]
# sort each list in the adj_temp_list and append to adj_list
for lst in adj_temp_list:
lst.sort()
adj_list.append(lst)
# return the adjacency list
return adj_list
def maximal_path(file_name,start):
# call adjacency_list function to get adjacency list
adj_list=adjacency_list(file_name)
# create max_path as an empty list
max_path=[]
# create a boolean list that represents which vertex is added to path
# Thus, initialize the list such that it represents no vertex is added to path
visited=[False]*len(adj_list)
# create an empty list(stack)
stack=[]
# add start vertex to stack list
stack.append(start)
# process until there is no possibility to extend the path
while True:
# get a node u from stack and remove it from stack
u=stack[0]
stack=stack[1:]
# set vertex u visited and add it to max_path
visited[u]=True
max_path.append(u)
# find whether u has unvisited neighbours to extend path or not
existNeighbour=False
for v in adj_list[u]:
if visited[v]==False:
existNeighbour=True
break
# if u has unvisited adjacent vertices
if existNeighbour==True:
# push all un-visited neighbours into stack
for v in adj_list[u]:
stack.append(v)
# if u has no unvisited adjacent vertices, exit the loop
else:
break
# return the maximal path
return max_path
Your approach is what's known as a Breadth First Search, or BFS. When you append new nodes to the end of the stack, the graph is explored layer by layer. The most minimal change you can make to your program to get your expected output is to insert the new nodes in the beginning of the stack.
stack.insert(0,v)
Related
So essentially it is a simple two sum problem but there are multiple solutions. At the end I would like to return all pairs that sum up to the target within a given list and then tally the total number of pairs at the end and return that as well. Currently can only seem to return 1 pair of numbers.
So far my solution has to been to try and implement a function that counts the amount of additions done, and while that number is less than the total length of the list the code would continue to iterate. This did not prove effective as it would still not take into account other solutions. Any help would be greatly appreciated
I took your code and did a couple of tweaks to where summations were being tested and how the data was being stored. Following is your tweaked code.
def suminlist(mylist,target):
sumlist = []
count = 0
for i in range(len(mylist)):
for x in range(i+1,len(mylist)):
sum = mylist[i] + mylist[x]
if sum == target:
count += 1
worklist = []
worklist.append(mylist[i])
worklist.append(mylist[x])
sumlist.append(worklist)
return count, sumlist
list = [0, 5, 4, -6, 2, 7, 13, 3, 1]
print(suminlist(list,4))
Things to point out.
The sumlist variable is defined as a list with no initial values.
When a summation of two values in the passed list equate to the test value, they are placed into a new interim list and then that list is appended to the sumlist list along with incrementing the count value.
Once all list combinations are identified, the count value and sumlist are returned to the calling statement.
Following was the test output at the terminal for your list.
#Dev:~/Python_Programs/SumList$ python3 SumList.py
(2, [[0, 4], [3, 1]])
To split the count value out from the list, you might consider splitting the returned data as noted in the following reference Returning Multiple Values.
Give that a try to see if it meets the spirit of your project.
You can use the itertools module for this job.
my_list = [1, 2, 3, 4]
target = 3
out = [x for x in itertools.combinations(my_list, r=2) if sum(x) == target]
print(out)
>>> [(0, 3), (1, 2)]
If you feel like using a python standard library import is cheating, the the official documentation linked above showcases example code for a "low level" python implementation.
Issue:
The issue for returning one set of possible several sets remains in the first return line (return sumlist). Based on the code, the function will automatically ends the function as the first set of value that their sum is the same as the target value. Therefore, we need to adjust it.
Adjustment:
I add a list(finallist[]) at the begining of the function for collecting all the applicable sets that can sum up to the target value. Then, I add a list(list[]) right after the if statement (*since I create an empty list(list[]) right after the if statement, when any sum of two values fulfills the target value, the function will empty the list again to store the new set of two values and append to the finallist[] again). Hence, as long as a set of two numbers can sum up to the target value, we can append them to the list(list[]). Accordingly, I add two more lines of code to append two values into the list(list[]). At the end, I append this list(list[]) to finallist[]. Also, I move the return statement to the final line and adjust the spacing. After this adjustment, the function will not end right after discovering the first possible set of values. Instead, the function will iterate repeatedly until getting all sets of the values and storing in the finalist[].
Originally, the function puts the return statement (return -1) at the end of the function for the situation that none of the sets can sum up to the target value. However, after the previous adjustment, the original return statement (return -1) will not have the opportunity to function as everything will end in the previous return line (return finallist). Therefore, I change it to the else part in the if statement (*meaning: when none of the sum of two values adds up to the target value, we will return 'No two values in the list can add up to the target value.')
Changes in Function:
def suminlist(mylist,target):
# count = 0 # delete
# while count < len(mylist): # delete
finallist=[] # add
for i in range(len(mylist)):
for x in range(i+1,len(mylist)):
sum = mylist[i]+mylist[x]
# count = count + 1 # delete
if sum == target:
# sumlist = mylist[i],mylist[x] # delete
# return sumlist # delete
list=[] # add
list.append(mylist[i]) # add
list.append(mylist[x]) # add
finallist.append(list) # add
else: # add
return 'No two values in the list can add up to the target value.' # add
return finallist # add
# return -1 # delete
Final Version:
def suminlist(mylist,target):
finallist=[]
for i in range(len(mylist)):
for x in range(i+1,len(mylist)):
sum = mylist[i]+mylist[x]
if sum == target:
list=[]
list.append(mylist[i])
list.append(mylist[x])
finallist.append(list)
else:
return 'No two values in the list can add up to the target value.'
return finallist
Test Code and Output:
list = [0, 5, 4, -6, 2, 7, 13, 3, 1]
print(suminlist(list,100))
# Output: No two values in the list can add up to the target value.
print(suminlist(list,4))
# Output: [[0, 4], [3, 1]]
I have been struggling with the following problem: I have a DFS outputted list:
[0.2500000074505806,
0.6500000059604645,
0.15000000223517418,
0.45000000298023224,
0.45000000298023224,
0.8499999940395355,
0.15000000223517418]
and want to transform this to a BFS ordering without first creating a tree and then applying BFS. For reference, this is a complete binary graph with depth 2.
Thanks in advance for the help.
For general graphs, DFS doesn’t contain enough information to obtain the BFS output. But if you’re sure the graph is a complete binary tree on 7 nodes, and you ran DFS from the root and output is x1,x2,x3,…,x7, then the BFS output would be x1,x2,x5,x3,x4,x6,x7.
More generally, if you have the DFS output for a complete binary tree on n nodes, the permutation that gives the BFS output can be generated by the following algorithm:
k = 3 # number of levels in complete binary tree
n = 2**k #so node labels are 1,2,...,n-1
L = list(range(1, n))
def toBFS(L):
#input: a sequence of node labels, obtained by DFS on a complete binary tree
#output: the sequence of node labels if BFS was performed
#Q = a queue of all sublists to be processed
#each sublist q has length 2^k-2
#process each sublist by printing 1st, and n/2th element
#and partitioning into two subsublists, both added to queue
print(L[0])
Q = []
Q.append(L[1:len(L)])
while len(Q) > 0:
q = Q.pop(0) #removes first element of Q
if len(q) <= 2:
for x in q:
print(x)
else:
print(q[0])
n = len(q)
print(q[n//2])
Q.append(q[1:n//2])
Q.append(q[n//2+1:n])
toBFS(L)
Output:
1
2
5
3
4
6
7
The algorithm takes as input the DFS sequence, and prints the root node, then does the following for each sublist in the FIFO queue: prints the left child, then adds about half of the elements as a sublist to a queue, then prints the right child, then adds about half of the elements as a sublist to a queue.
When the tree is guaranteed to be a perfect binary tree, i.e. where the leaves of the tree are all on the bottom level, then you could use a pragmatic approach where you populate the levels of the level order traversal as separate lists, and then return the concatenation of those values:
def preorder_to_levelorder(seq):
height = int(len(seq) ** 0.5)
levels = [[] for _ in range(height+1)]
it = iter(seq)
def recur(depth):
if depth > height:
return
try:
val = next(it)
except:
return
levels[depth].append(val)
recur(depth + 1)
recur(depth + 1)
recur(0)
return [val for level in levels for val in level]
Example perfect tree:
4
/ \
2 6
/ \ / \
1 3 5 7
Example run for that tree:
preorder = [4, 2, 1, 3, 6, 5, 7]
print(preorder_to_levelorder(preorder)) # [4, 2, 6, 1, 3, 5, 7]
I need to move single or multiple sequential items in a list, by sequential I mean if the items are more than one there are no gaps in their list indexes, here is what I have tried...
def move(x, tomove, idx):
move_start = tomove[0]
move_end = tomove[-1] + 1
if idx == len(x) or idx == len(x) - 1:
return x[:move_start] + x[move_end:] + x[move_start:move_end]
elif idx == 0:
return x[move_start:move_end] + x[:move_start] + x[move_end:]
else:
pass
# move to start
print (move([0,1,2,3,4,5,6],
[2],
0))
# expected output [2,0,1,3,4,5,6]
# move to end
print (move([0,1,2,3,4,5,6],
[2],
6))
# expected output [0,1,3,4,5,6,2]
# move forward single
print (move([0,1,2,3,4,5,6],
[2],
3))
# expected output [0,1,3,2,4,5,6]
# move backward single
print (move([0,1,2,3,4,5,6],
[2],
1))
# expected output [0,2,1,3,4,5,6]
# move forward multiple
print (move([0,1,2,3,4,5,6],
[2,3],
5))
# expected output [0,1,4,5,2,3,6]
# move backward multiple
print (move([0,1,2,3,4,5,6],
[4,5],
2))
# expected output [0,1,4,5,2,3,6]
Since you've not mentioned anything about validation like if the given sequence of elements is not present in the original list, the ordering of the main list and given list etc. I've not considered any validations.
Before jumping into code, let's see how to crack the solution.
First, you need to identify the existing position of the given list in the main list.
move_list_pos = lis.index(move_list[0])
Now, check whether the given list to be moved forward or backward.
If it needs to move backward, then theirs no change in the main list
till the new positon, insert the given list, continue the main list except for the given list.
If it needs to move forward, then the main list should be there
except for the given list in the main list till the new position, add the
given list, continue the main list from a new position to end.
Solution code:
def move(lis,move_list,pos):
move_list_pos = lis.index(move_list[0])
if pos<move_list_pos:
return lis[:pos]+move_list+lis[pos:move_list_pos]+lis[move_list_pos+len(move_list):]
else:
return lis[:move_list_pos]+lis[move_list_pos+len(move_list):pos+1]+move_list+lis[pos+1:]
I'd like to find a convenient way to get the length of a list in an adjacency list.
Creating an empty list and appending a list from the adjacency list into it doesn't work well.
The original code without creating an empty list within the function:
def road_map(point_map, n):
criteria_met = 0
p = 0
for p in point_map:
points = len(point_map[p])
for i in point_map[p]:
if len(point_map[p]) >= n:
criteria_met += 1
p += 1
return criteria_met
For example:
there's a train station (0), an airport (1), an office tower (2) and a hotel (3) in a certain part of a town, as in:
town_points = [[1, 3], [0, 2], [1], [0]]
and we want:
print(road_map(town_points, 2))
to find areas connected to at least 2 other buildings specified.
This happens:
Traceback (most recent call last):
File "C:/Users/user/PycharmProjects/untitled1/roadmap.py", line 14, in <module>
print(road_map(town_points, 2))
File "C:/Users/user/PycharmProjects/untitled1/roadmap.py", line 5, in road_map
points = len(point_map[p])
TypeError: list indices must be integers or slices, not list
It might seem convenient and more conventional to use sub-functions to grab the list from the adjacency list, but I'm doubting its usefulness.
Let's break down your problem:
You want to find the length of each list in adjacency list
You want to count the number of lists with length >= 2
First, let's take a look at the road_map function you provided. The lines p=0 and p+=1 isn't needed, since the loop for p in point_map: will replace the p with elements of point_map list. Hence, the function can be simplified into:
def road_map(point_map, n):
criteria_met = 0
for p in point_map:
points = len(point_map[p])
for i in point_map[p]:
if len(point_map[p]) >= n:
criteria_met += 1
return criteria_met
However, this will still yield TypeError: list indices must be integers or slices, not list. There are several problems in this code, but let's focus on the cause of the error. The loop for p in point_map means in each iteration, p is an element of point_map. In your example, in the first iteration, p holds the value [1,3], and then in the next iteration, [0,2], and so on.
A python list can only be indexed with an integer and not another list. When we access point_map[p] in the first iteration, we're using p which is a list [1,3] as the index.
Since p is already the list of adjacent nodes in each iteration, we can use len(p) to know the number of adjacent nodes of each node. Hence, the code to find areas connected to at least 2 other buildings specified can be written as:
def road_map(point_map, n):
criteria_met = 0
for p in point_map:
if len(p) >= n:
criteria_met += 1
return criteria_met
or, in a far more compact version:
def road_map(point_map, n):
return len([l for l in point_map if len(l)>=n])
In this code [l for l in point_map if len(l)>=n] iterates through every single element l of point_map, which in this case is always a list. Then, it filters out every l that does not meet the criterion len(l)>=n. After that, we use len([l for l in point_map if len(l)>=n]) to calculate the length of the resulting list.
Here is a fragment of the code responsible for creating the graph and its edges, depending on whether the edge exists and the condition that validates the shortest paths:
for q in range(len(aaa_binary)):
if len(added)!=i+1:
g.add_nodes_from (aaa_binary[q])
t1 = (aaa_binary[q][0],aaa_binary[q][1])
t2 = (aaa_binary[q][1],aaa_binary[q][2])
t3 = (aaa_binary[q][2],aaa_binary[q][3])
if g.has_edge(*t1)==False and g.has_edge(*t2)==False and g.has_edge(*t3)==False:
g.add_edge(*t1)
g.add_edge(*t2)
g.add_edge(*t3)
added.append([aaa_binary[q],'p'+ str(i)])
for j in range(len(added)):
if nx.shortest_path(g, added[j][0][0], added[j][0][3])!=added[j][0] or nx.shortest_path(g, aaa_binary[q][0], aaa_binary[q][3])!=aaa_binary[q]:
g.remove_edge(*t1)
g.remove_edge(*t2)
g.remove_edge(*t3)
added.remove([aaa_binary[q],'p'+ str(i)])
break
if g.has_edge(*t1)==False and g.has_edge(*t2)==False and g.has_edge(*t3)==True:
g.add_edge(*t1)
g.add_edge(*t2)
added.append([aaa_binary[q],'p'+ str(i)])
for j in range(len(added)):
if nx.shortest_path(g, added[j][0][0], added[j][0][3])!=added[j][0] or nx.shortest_path(g, aaa_binary[q][0], aaa_binary[q][3])!=aaa_binary[q]:
g.remove_edge(*t1)
g.remove_edge(*t2)
added.remove([aaa_binary[q],'p'+ str(i)])
break
# ... and then the rest of the False and True possibilities combinations in the `if g.has_edge()'condition.
added[] - list of currently valid paths in the form [[[0, 2, 4, 6], 'p0'], [[0, 2, 4, 1], 'p1'],...]
aaa_binary[] - list of path combinations to check in the form [[0, 2, 4, 6], [0, 2, 6, 4], [0, 4, 2, 6],...]
Loop operation:
The algorithm selects one sublist from the aaa_binary list, then adds nodes to the graph and creates edges. Then the algorithm checks if the given edge exists. If it does not exist, it adds it to the graph, if it exists, it does not add. Then, if the condition of the shortest path is not met, only the newly added edge is removed from the graph. And so until you find the right path from the aaa_binary list.
As you can see only with the four-element sublistors, there are 8 different combinations of False and True in the condition if g.has_edge () in the aaa_binary list, which already makes a technical problem. However, I would like to develop this to check, for example, eight-element paths, and then the combination will be 128! Which is obvious that I can not do it in the current way.
And I care that the loop necessarily adds only non-existent edges, because then it is easier to control the creation of the optimal graph.
Hence my question, is it possible to write such a loop differently and automate it more? I will be very grateful for any comments.
How about:
added_now = []
for edge in (t1,t2,t3):
if not g.has_edge(*edge):
g.add_edge(*edge)
added_now.append(edge)
added.append([aaa_binary[q],'p'+ str(i)])
for j in range(len(added)):
if nx.shortest_path(g, added[j][0][0], added[j][0][3])!=added[j][0] or nx.shortest_path(g, aaa_binary[q][0], aaa_binary[q][3])!=aaa_binary[q]:
for edge in added_now:
g.remove_edge(*edge)
added.remove([aaa_binary[q],'p'+ str(i)])
You just want to do the same for each edge that wasn't added.
Does this solution suits you ?
It doesn't block you from your 4-elem paths. It adjusts to the len of the current aaa_binary[q]. If you want to choose a n-elem paths, it should be easily modifiable. :)
It doesn't have a non-ending list of if.
for q in range(len(aaa_binary)):
if len(added)!=i+1:
g.add_nodes_from(aaa_binary[q])
# Instead of having hard-coded variable, make a list.
tn = []
for idx in range(0, len(aaa_binary[q]) - 1):
# Linking the current elem, to the next one.
# The len() - 1 avoids the iteration on the last elem,
# that will not have another elem after it.
tn.append((aaa_binary[q][idx], aaa_binary[q][idx + 1]))
# Instead of checking each and every case, try to make your
# task 'general'. Here, you want to add the edge that doesn't exist.
indexSaver = []
for index, item in enumerate(tn):
if g.has_edge(*item):
g.add_edge(*item)
# This line is here to keep in mind which item we added,
# Since we do not want to execute `.has_edge` multiple times.
indexSaver.append(index)
# This line is quite unclear as we do not know what is 'added',
# neither 'i' in your code. So I will let it as is.
added.append([aaa_binary[q], 'p' + str(i)])
# Now that non-existent edges have been added...
# I don't understand this part. So we will just modify the [3]
# index that was seemingly here to specify the last index.
for j in range(len(added)):
lastIndex = len(added) - 1
if nx.shortest_path(g, added[j][0][0], added[j][0][lastIndex])!=added[j][0] or nx.shortest_path(g, aaa_binary[q][0], aaa_binary[q][lastIndex])!=aaa_binary[q]:
# On the same logic of adding edges, we delete them.
for idx, item in enumerate(tn):
if idx in indexSaver:
g.remove_edge(*item)
added.remove([aaa_binary[q], 'p' + str(i)])
break