depth first search using constraints - python

I'm trying to implement a dfs algorithm to traverse a graph. The goal of this traversal is to find paths. In my case a graph is constructed in which each node represents a city in europe. Nodes are connected to nodes of neighbouring countries (cities in the neighbouring countries). The goal is to find paths throughout europe where the journey starts at a city in a certain country starting with an 'a'. Then move on to a neighbouring country to a city starting with a 'b', then go to a neighbouring country of the last country and move to a city starting with a 'c' etc.
e.g (Amsterdam, Netherlands) --> (Berlin, Germany) --> (Cannes, France) --> etc.
I have constructed a graph in which all cities from a certain country are connected with the cities of the neighbouring countries. In order to find these paths dfs seemed like a logical choice to me. However, I am not sure how to implement certain constraints in this algorithm. For example I do not want to traverse through the graph further if a node (city) does not start with the wanted letter. For example, if I am coming from a city starting with a 'b' I dont want to traverse further when the next node is a 'd'. Also I do not want to visit the same country multiple times. Can someone maybe give me some guidance on how to implement these types of constraints in a dfs model? Below I have a very-bones implementation of dfs and I am wondering where should build these conditions.
visited = set()
def dfs(visited, graph, root):
if root not in visited:
print(root)
visited.add(root)
for neighbour in graph[root]:
dfs(visited, graph, root)
Thank you for your valuable time in advance!
Kind regards, Jop

The main two things I would reccomend are a helper function to check if a destination is valid and a record of the path you're travelling.
In similar psuedo code, I would recommend something like this:
visited = set()
path = [startingDestination]
def validDestination(from, to):
if countriesNeighborCheck and cityLetterCheck:
return True
else:
return False
def dfs(visited, graph, root, path):
if root not in visited:
if validDestination(path[-1], root):
path.append(root)
visited.add(root)
for neighbour in graph[root]:
return dfs(visited, graph, root, path)

Related

Get all possible DFS visits from a given start

let's say I have the following undirected graph:
V = {v1,v2,v3,v4}
E = {{v1,v2}, {v1,v3}, {v2,v4}, {v3,v4}}
If I run a DFS starting in v1 without a destination, just to visit every point of the graph, there are two possibilities: v1->v2->v4->v3 or v1->v3->v4->v2.
I wrote a short python program that does one path of DFS:
def custom_dfs(path, graph, current):
if(current in path):
return None
path.append(current)
for possibility in graph[current]:
custom_dfs(path, graph, possibility)
Now my question is, how to extend this so that I can account for all possibilities, in other word to run DFS on each connected node?
Here is the algorithm to find a spanning tree
add start node to span
while nodes outside span
loop n1 over nodes in span
loop n2 over nodes not in span
if n1-n2 link present
add n2 to span
Modified like this should ( I haven't tested it ) enumerate all possible spans
add start node to span
push partial span to stack
while stack not empty
pop partial span from stack
new_span_found = false
while nodes outside span
loop n1 over nodes in span
loop n2 over nodes not in span
if n1-n2 link present
add n2 to span
push partial span to stack
new_span_found = true
if new_span_found
add span to output

Walk through decision tree and capture each node

I'm trying to walk through a Decision Tree one node at a time.
Each node can have 2-3 paths
On the nodes with 3 paths, one of the paths is always an end point, and one is sometimes and end point
We can't move backwards, but can start over
Our available functions are
getCurrentNode() #returns string of current node's path from start (ex. 'A-B-A-A-B')
getCurrentNodePaths() #returns number of possible paths from this node
startOver() #puts us back at node 0
takePath(int pathNumber) #traverse the decision tree down a desired path
I've written this pseudo code that should walk through each node recursively, but only for the 'left' most path
# Start
def walk(pathNumber):
takePath(pathNumber)
next_nodes_paths = getCurrentNodePaths()
if next_nodes_paths.length > 0:
walk(0)
startOver()
walk(0)
How can I get this to keep track of where it's been, start over, and take a new path
This creates a model of the decision tree. You can navigate to a certain Node with the select_path method. path is a string like '03231002'. You can walk over the whole tree and apply a function at each point, by using the apply_function method. There is an example for walking the whole tree.
def select_path(path):
startOver()
for pathNumber in path:
takePath(int(pathNumber))
class Node:
def __init__(self,path):
self.path = path
self.select()
self.num_children = getCurrentNodePaths().length
self.children = [Node(path+str(i)) for i in range(self.num_children)]
def select(self):
select_path(self.path)
def apply_function(self, func, recursive=False):
self.select()
func()
if recursive:
for child in self.children:
apply_function(self, func, recursive=True)
root = Node('')
#walk whole tree and apply function function:
#def function:
# pass
#root.apply_function(function, recursive=True)
Since we cannot move backwards, your approach of (recursive) depth-first search is probably difficult to handle: You can never know if a node is an end node without actually moving there, and once you have arrived, you can only make a new walk to the previous node all the way from the start.
I suggest using a breadth-first search instead (partly adopted from this example):
def walk(currentNode):
queue = []
visited = []
queue.append(currentNode)
visited.append(currentNode)
while queue:
s = queue.pop(0)
# go to node from start by following the path
startOver()
for p in s:
takePath(int(p))
for i in range(getCurrentNodePaths()):
nextNode = getCurrentNode() + str(i)
queue.append(nextNode)
visited.append(nextNode)
# Use this if you want a list of paths to end points only
# if getCurrentNodePaths() > 0:
# visited.remove(s)
print(visited)
startOver()
walk(getCurrentNode())
This will give you a list of paths to all nodes in your tree in visited.
A few notes:
A node in the queue and visited lists is assumed to be represented by its path from the start node as a string (e.g. 0, 101, 012012).
Thus, a node can be reached by just following its sequence of numbers.
Moreover, the successor nodes can be constructed by
appending the numbers within range(getCurrentNodePaths()).

Checking for a loop in an un-directed tree graph python

I am trying to develop a python program to check if there is a loop in the graph when a new line is added.
I am storing different lines in a list ordered by shortest length first, and the lines are a class:
class Line():
def __init__(self,node1,node2,length):
self.node1 = node1
self.node2 = node2
self.length = int(length)
self.drawn = False
And the nodes are stored in a list:
nodes = ["A","B","C","D","E","F"]
When my program has run it stores the route as a list:
route = [class(Line),class(Line)...]
What i want it to do is to check that when a new line is added that it does not form a cycle. I plan to use a method inside of a bigger class:
something like:
def check_loop(new_line,graphs):
add new line to graph
if there is a loop in graphs:
return False
else:
return True
(sorry this is one of my first posts so the format is rubbish)
To identify if a cycle is created in a tree is pretty simple, all you need to do is pick a node and then breadth first search the entire tree from that node. If any node has been visited before by the time you reach it, then you know that there is a cycle because there was an alternative path that reached that node beside the one that you have taken.

Large graph as input to find all paths

I'm using the following python code in order to find all paths between each two nodes. It's not any problem for small graphs.
def bfs(graph, start, end):
# maintain a queue of paths
queue = []
# push the first path into the queue
queue.append([start])
while queue:
# get the first path from the queue
path = queue.pop(0)
# get the last node from the path
node = path[-1]
# path found
if node == end:
return path
# enumerate all adjacent nodes, construct a new path and push it into the queue
for adjacent in graph.get(node, []):
new_path = list(path)
new_path.append(adjacent)
queue.append(new_path)
for node3 in graph:
for node4 in graph:
few = bfs(graph, node3, node4)
if not few == None:
print ("{0} -> {1}".format(node3,node4))
print (few)
print ("\n")
But, I want to find all paths between each two nodes, for a large graph with about 4K nodes and 20K edges. The program intrupts and doesn't return any output.
May you please help me how to set the input graph and also, how to set the output to add in a separated file?
Your answer is it may couldn't be done, except the case that your graph is a special graph, number of paths between two nodes in such graph may be enormous.consider following case:
For a complete graph of 200 vertices and 20K edges there is 198!/2 different paths between any two vertices.
If your graph contains a cycle then there is infinite paths in it.
Your graph could be have such number of paths between two vertices that even a super computer couldn't compute the number in a decade.

When to use Depth-first search

So I want to sort a range of numbers so that the sum of every sequential number is a squared number (4,9,16,25,...) I was told I should use Depth first search but am unable to implenment it correctly, especialy the backtracking part. Is this the correct way to do this, what am I not seeing, doing wrong? Is DFS even the correct way to do this?
class Node: #Define node Object
def __init__(self, value):
self.value = value #Node "Name"
self.adjacentNodes=list() #Node Neighbours
#Set limit
limit=16
squares=list() #list for squared numbers in rang smaller than 2Xlimit
tree=list() #Tree containing nodes
path=list() #current path taken
def calculate(limit): #Population control legal squares
for i in range(1,(limit+1)):
i=i*i
if i<(limit+limit):
squares.append(i)
calculate(limit) #Populate squares list
for a in range(1,(limit+1)): #creating the nodes
newnode=Node(a)
for s in squares:
legalsum=s-a
if legalsum>0 and legalsum<limit and legalsum!=a: #zero and lower can't play,keeping non-exiting nodes out of node.adjacentNodes, don't connect to selve.
newnode.adjacentNodes.append(legalsum) #Add adjacentnode
tree.append(newnode) #add newborn to tree
for b in range(0,limit):
print tree[b].value,tree[b].adjacentNodes #Quick checkup
#Now the tree is build and can be used to search for paths.
'''
Take a node and check adjnodes
is it already visited? check path
yes-> are all nodes visited (loop over tree(currentnode).adjnodes) Maybe use len()
yes->pop last node from path and take other node -> fear for infinte loops, do I need a current node discard pile?
no -> visit next available adje node next in line from loop got from len(tree.adjn)
no-> take this as node and check adj nodes.
'''
def dfs (node):
path.append(node)
#load/check adjacent nodes
tree[node].adjacentNodes
for adjnode in tree[node-1].adjacentNodes:
if adjnode in path:
#skip to next adj node
continue
else:
print path #Quick checkup
dfs(adjnode)
dfs(8) # start with 8 since 1-16 should be {8,1,15,10,6,3,13,12,4,5,11,14,2,7,9,16}
I have no idea what you are talking about with the squares but here are some problems
def dfs (node):
#always assume the path starts with this node
path=[node]
if node == GoalState: return path #we win!!
#load/check adjacent nodes
#this line did nothing `tree[node].adjacentNodes`
for adjnode in tree[node-1].adjacentNodes:
# if adjnode in path: ****this will never be true, lets eliminate it
# #skip to next adj node
# continue
# else:
subpath = dfs(adjnode)
if subpath and subpath.endswith(GoalState):
return path + subpath #return solution
#if we get here there is no solution from this node to the goal state

Categories