Using DFS Recursively to Find a Particular Path - python-3.x

I am attempting to use DFS to solve a graph problem, and have ran into a wall. This is my implementation in Python3. unvisited is an array of ints, start and end are ints in unvisited, path is an empty array filled out as the DFS goes, and edges is a dictionary of edges.
def traverse(unvisited, start, end, path, edges):
copy_unvisited = unvisited.copy()
copy_path = path.copy()
current = start
copy_unvisited.remove(current)
copy_path.append(current)
if current == end and len(copy_unvisited)==0:
#print is just for me to check my answers
print(copy_path)
return copy_path
for i in edges[current]:
if i in copy_unvisited:
return traverse(copy_unvisited, i, end, copy_path, edges)
The goal is to find a path that starts on start and visits every int in unvisited until ending on end. Therefore, I'm running into a problem with the recursion (I think) because in cases where the path is wrong, I don't want to return anything; instead, I want the DFS to continue. With the way the code is written now, I'm getting "None" returned, even though the correct solution is being printed (because of my print statement). How can I fix this issue?
Thanks in advance!
Edit: If unvisited = [1,3,4,5], start = 4, end = 5, edges = {1: (4,5), 3: (1), 4: (1,3,5), 5: (1,4)},
traverse(unvisited, 4, 5, [], edges) should return [4,3,1,5], but instead I get None. traverse does not have a return type if a wrong path is followed, which is why I think I'm getting None. It eventually finds the right path and prints it.
I have found a solution by introducing a new parameter sol, and when the correct path is reached, I copy each entry of copy_path into sol. Then, I return sol at the END, after the recursive call. I also removed the return statement for the recursive call.
def traverse(unvisited, start, end, path, edges,sol):
copy_unvisited = unvisited.copy()
copy_path = path.copy()
current = start
copy_unvisited.remove(current)
copy_path.append(current)
if current == end and len(copy_unvisited)==0:
for i in copy_path:
sol.append(i)
for i in edges[current]:
if i in copy_unvisited:
traverse(copy_unvisited, i, end, copy_path, edges,sol)
return sol
Feels a little inelegant, so I'm open to better ways to solve this problem!

First of all, with the input that you gave, the output should be [4 1 5], since the first edge linked to the vertex 4 is 1 and at the time of the first execution of the DPS 1 still in the unvisited list.
The main problem with your original code is these two instructions:
The
len(copy_unvisited)==0:
in the first if and the
if i in copy_unvisited:
at the end.
Basically you have this:
First call of the function:
node 4, unvisited (uv): [1,3,4,5], path=[]
At the for: 4 uv: [1,3,5] edges: (1, 3, 5)
So i=1, i is in uv, then:
Second call of the function:
node 1, uv: [1,3,5], path=[4]
At the for: 1 uv: [3,5] edges: (4,5)
So i=4, but 4 is not in uv,
So i=5, 5 is in uv, then:
Third call of the function *OBS1*
node 5, uv: [3,5], path=[4,1]
it will not enter in the first if, because len(uv) is not equals 0.
At the for: 5 uv: [3] edges: (1,4)
So i=1, but 1 is not in uv
So i=4, but 4 is not in uv
The for ends, so the function ends and not call itself, neither return a value.
I am pretty sure the DPS should end when you get in the wanted node, or, when you have no more nodes to visit. In the first case, it should return the path it found, if not, it means the end node there is no path from the start node to the end node (you have it in a disconnected graph).
I tried modify your implementation, but I found it difficult to deal with this "unvisited" variable and with the path being updated in this way. I don't think you are conceptually incorrect, both in the overview of how DFS should work, and in how to update the recurrence, but I was unable to make it work that way and so I modified the algorithm so that it updated the path to as it unstack the functions, instead of doing so while it calls the functions.
def traverse(start,end,edges,visited=[],path = []):
if start == end:
path.append(start)
return path
if start not in visited:
visited.append(start)
if start not in edges:
return path
for i in edges[start]:
path = traverse(i,end,edges,visited,path )
if len(path)>0:
break
if len(path)>0 and start not in path:
path.append(start)
return path
This will return the path, written in reverse, if you want in the normal order just take the final result of the function, which will be a list and apply the .reverse () method on it.
This implementation above is a slight modification of the one I found here: https://likegeeks.com/depth-first-search-in-python/ I just added the part of finding a path between two specific points.
Your "sol" solution work, partially, because, when the DFS get into the the third call and left the for without calling the function again, it, at least can return a empty list, then the third call leaves the stack and the algorithm continues to the next i of the second function call, that is 3. The problem with this implementation is the DFS will read all nodes before return a path to the destination.

Related

DC3/Skew Suffix Array Algorithm doesn't work for specific cases

When applying the DC3/Skew algorithm to the string yabadabado, I can't quite get it to sort correctly. This issue happens in other cases, but this is a short example to show it.
This first table is for reference:
These are the triples of R12
We have a tie between i = 1, and i = 5 since both their triples are aba.
We now need to get the suffix array of the ranks R' through recursing, but we can quickly break this tie since i_1 = [1,3] > i_5 = [1,2] which implies that the suffix starting at i = 5 should come before i = 1. Recursing returns the same result with R'5 < R'1.
So applying these results puts the relative order of those two suffixes as:
S[5,] == abado // Comes first (smaller)
S[1,] == abadabado
But abadabado < abado. I've been looking at this for a while, and can't seem to figure out where I stray from the algorithm.
I'm hoping someone with more experience using the algorithm can point me in the right direction.

Python list slice [0:-1:-1]

I ran into this statement while I was coding:
l = [1,2,3]
print(l[0:-1:-1])
I was expecting this piece of code gives me [1] however it gives me [], makes me think I must have mis-understood python slice operation, can someone explain what is going on here?
In a slice,
The first integer is the index where the slice starts.
The second integer is the index where the slice ends.
The third integer specifies a stride or step causing the resulting slice to skip items. -1 for reverse the output.
l[0:-1:-1]
is equivalent to
l[len(l)-1:len(l)-1:-1]
The first index converted 0 to len(l)-1, because you added -1 in the last index to reverse the list. This will always give you an empty list.
When you use slice in python and you type
l[x:y:-1]
it would somehow be equivalent to
l.reverse()
print(l[y:x])
l.reverse()
but with the difference that -1 reverses the list elements with their indexes
so if you type
l=[1, 2, 3]
l[2:0:-1]
the output will be
[3, 2]
The reason for empty list is that you change the order of indexes so it wont find any element in that window...
I gathered some information after talking with friends about this problem, now we kind of believe this is what happened: when I do l[0:-1:-1], the first thing interpreter will do is to convert that second -1 to the positive index, which is 2 in my case. Then it will iterate backward with the terminating condition being "start <= end", in this case since at the very beginning it will find 0 <= 2, so it will directly terminate, results in the output array being empty.
I didn't really get what the two other answers are saying here (silly me), maybe they are right, I need to look into the python source code for a 100% certain explanation, but for now I believe what I just stated here is the case.

Why is my merge sort algorithm not working?

I am implementing the merge sort algorithm in Python. Previously, I have implemented the same algorithm in C, it works fine there, but when I implement in Python, it outputs an unsorted array.
I've already rechecked the algorithm and code, but to my knowledge the code seems to be correct.
I think the issue is related to the scope of variables in Python, but I don't have any clue for how to solve it.
from random import shuffle
# Function to merge the arrays
def merge(a,beg,mid,end):
i = beg
j = mid+1
temp = []
while(i<=mid and j<=end):
if(a[i]<a[j]):
temp.append(a[i])
i += 1
else:
temp.append(a[j])
j += 1
if(i>mid):
while(j<=end):
temp.append(a[j])
j += 1
elif(j>end):
while(i<=mid):
temp.append(a[i])
i += 1
return temp
# Function to divide the arrays recursively
def merge_sort(a,beg,end):
if(beg<end):
mid = int((beg+end)/2)
merge_sort(a,beg,mid)
merge_sort(a,mid+1,end)
a = merge(a,beg,mid,end)
return a
a = [i for i in range(10)]
shuffle(a)
n = len(a)
a = merge_sort(a, 0, n-1)
print(a)
To make it work you need to change merge_sort declaration slightly:
def merge_sort(a,beg,end):
if(beg<end):
mid = int((beg+end)/2)
merge_sort(a,beg,mid)
merge_sort(a,mid+1,end)
a[beg:end+1] = merge(a,beg,mid,end) # < this line changed
return a
Why:
temp is constructed to be no longer than end-beg+1, but a is the initial full array, if you managed to replace all of it, it'd get borked quick. Therefore we take a "slice" of a and replace values in that slice.
Why not:
Your a luckily was not getting replaced, because of Python's inner workings, that is a bit tricky to explain but I'll try.
Every variable in Python is a reference. a is a reference to a list of variables a[i], which are in turn references to a constantant in memory.
When you pass a to a function it makes a new local variable a that points to the same list of variables. That means when you reassign it as a=*** it only changes where a points. You can only pass changes outside either via "slices" or via return statement
Why "slices" work:
Slices are tricky. As I said a points to an array of other variables (basically a[i]), that in turn are references to a constant data in memory, and when you reassign a slice it goes trough the slice element by element and changes where those individual variables are pointing, but as a inside and outside are still pointing to same old elements the changes go through.
Hope it makes sense.
You don't use the results of the recursive merges, so you essentially report the result of the merge of the two unsorted halves.

Connect string value to a corresponding variable name

This question has somehow to do with an earlier post from me. See here overlap-of-nested-lists-creates-unwanted-gap
I think that I have found a solution but i can't figure out how to implement it.
First the relevant code since I think it is easier to explain my problem that way. I have prepared a fiddle to show the code:
PYFiddle here
Each iteration fills a nested list in ag depending on the axis. The next iteration is supposed to fill the next nested list in ag but depending on the length of the list filled before.
The generell idea to realise this is as follows:
First I would assign each nested list within the top for-loop to a variable like that:
x = ag[0]
y = ag[1]
z = ag[2]
In order to identify that first list I need to access data_j like that. I think the access would work that way.
data_j[i-1]['axis']
data_j[i-1]['axis'] returns either x,y or z as string
Now I need to get the length of the list which corresponds to the axis returned from data_j[i-1]['axis'].
The problem is how do I connect the "value" of data_j[i-1]['axis'] with its corresponding x = ag[0], y = ag[1] or z = ag[2]
Since eval() and globals() are bad practice I would need a push into the right direction. I couldn't find a solution
EDIT:
I think I figured out a way. Instead of taking the detour of using the actual axis name I will try to use the iterator i of the parent loop (See the fiddle) since it increases for each element from data_j it kinda creates an id which I think I can use to create a method to use it for the index of the nest to address the correct list.
I managed to solve it using the iterator i. See the fiddle from my original post in order to comprehend what I did with the following piece of code:
if i < 0:
cond = 0
else:
cond = i
pred_axis = data_j[cond]['axis']
if pred_axis == 'x':
g = 0
elif pred_axis == 'y':
g = 1
elif pred_axis == 'z':
g = 2
calc_size = len(ag[g])
n_offset = calc_size+offset
I haven't figured yet why cond must be i and not i-1 but it works. As soon as I figure out the logic behind it I will post it.
EDIT: It doesn't work for i it works for i-1. My indices for the relevant list start at 1. ag[0] is reserved for a constant which can be added if necessary for further calculations. So since the relevant indices are moved up by the value of 1 from the beginning already i don't need to decrease the iterator in each run.

Simple adding two arrays using numpy in python?

This might be a simple question. However, I wanted to get some clarifications of how the following code works.
a = np.arange(8)
a
array([1,2,3,4,5,6,7])
Example Function = a[0:-1]+a[1:]/2.0
In the Example Function, I want to draw your attention to the plus sign between the array a[0:-1]+a[1:]. How does that work? What does that look like?
For instance, is the plus sign (addition) adding the first index of each array? (e.g 1+2) or add everything together? (e.g 1+2+2+3+3+4+4+5+5+6+6+7)
Then, I assume /2.0 is just dividing it by 2...
A numpy array uses vector algebra in that you can only add two arrays if they have the same dimensions as you are adding element by element
a = [1,2,3,4,5]
b = [1,1,1]
a+b # will throw an error
whilst
a = [1,2,3,4,5]
b = [1,1,1,1,1]
a+b # is ok
The division is also element by element.
Now to your question about the indexing
a = [1,2,3,4,5]
a[0:-1]= [1,2,3,4]
a[1:] = [2,3,4,5]
or more generally a[index_start: index_end] is inclusive at the start_index but exclusive at the end_index - unless you are given a a[start_index:]where it includes everything up to and including the last element.
My final tip is just to try and play around with the structures - there is no harm in trying different things, the computer will not explode with a wrong value here or there. Unless you trying to do so of course.
If arrays have identical shapes, they can be added:
new_array = first_array.__add__(second_array)
This simple operation adds each value from first_array to each value in second_array and puts result into new_array.

Resources