Trouble returning bool from recursive function - python

I'm working on a homework problem in which we have to write an algorithm that can determine if a graph is bipartite or not. My python solution works, but right now it throws an exception if the graph is not bipartite, instead I would like it to return a bool. How could I modify this code?
def is_bipartite(v, visited, colors, counter):
print(v)
# Set this vertex to visited
visited[v] = True
colors[v] = counter % 2
# Explore links
for u in v.links:
# If linked up node u has already been visited, check its color to see if it breaks
# the bipartite of the graph
if u in visited:
if colors[v] == colors[u]:
raise Exception("Not Bipartite")
# If the link has not be visited then visit it
if u not in visited:
visited[u] = False
is_bipartite(u, visited, colors, counter + 1)

If I understand your code correctly, you want to return False if you get matching colors anywhere along your recursive search. You want to return True if you get to the end of the search without finding anything.
That is not too hard to do. Just change the raise statement to return False and check the result of the recursive calls, and return False if any of them return a False result. Then just put return True at the end of the function and you're done:
def is_bipartite(v, visited, colors, counter):
visited[v] = True
colors[v] = counter % 2
for u in v.links:
if u in visited:
if colors[v] == colors[u]:
return False # return instead of raise in this base case
if u not in visited:
visited[u] = False
if not is_bipartite(u, visited, colors, counter + 1): # check the recursion
return False # pass on any False
return True # return True only if you got to the end without returning False above

Related

Return immediately for recursive calls

Problem Statement:
You are given an integer array nums. You are initially positioned at the array's first index, and each element in the array represents your maximum jump length at that position. Return true if you can reach the last index, or false otherwise.
How can I change my code so that it returns immediately when I have found a path that works for this problem instead of going through all the recursive calls that I have made previously
def canJump(self, nums: List[int]) -> bool:
solve = [False]
def backtrack(i):
if solve[0] == True:
return
if i == len(nums)-1:
solve[0] = True
return
if i >= len(nums) or nums[i] == 0:
return
for x in range(1, nums[i]+1):
backtrack(i+x)
backtrack(0)
return solve[0]
General Form of a Recursive Function
The general form of a recursive function has two mutually exclusive types of conditions that can be met on each iteration of the recursion. These are either:
terminal conditions, or
non-terminal conditions.
Both types of condition contain a return statement.
Terminal Conditions
The return statement in terminal conditions typically takes the form return <value>.
The solution to the problem you are trying to solve requires two possible terminal conditions:
The case where you know you can reach the last index. return True
The case where you know you can NOT reach the last index. return False
Non-Terminal Conditions
The non-terminal condition will occur on iterations where neither of the terminal cases are met. In this situation, you will call the recursive function and return what it returns.
This answer covers terminal and non-terminal conditions in more detail.
Example
Consider a recursive function that sums the numbers of an array.
def sum(position, array, end):
if(position == end): # terminal condition
return 0
else: # non-terminal condition
return array[position] + sum(position+1, array, end)
Another Example
Depending on any constraints to your problem that I might have missed, a solution might be the following:
def jump(current_position, nums, finish_line):
"""
greedy algorithm:
choose the next position with the largest sum of (jump_range + index)
"""
jump_range = nums[current_position]
choice = current_position + jump_range
if(jump_range == 0): # terminal condition
return False
if(choice >= finish_line): # terminal condition
return True
else: # non-terminal condition
utility = 0
for next_position in range(current_position+1, jump_range+1):
next_jump_range = nums[next_position]
if(utility <= next_position + next_jump_range):
utility = next_position + next_jump_range
choice = next_position
return jump(choice, nums, finish_line)
input1 = [2,0,0,10,3]
input2 = [2,3,0,10,3]
current_position = 0
finish_line = len(input1)
print(jump(0, input1, finish_line)) # False
finish_line = len(input2)
print(jump(0, input2, finish_line)) # True
The most noteworthy difference from your solution is that return statements always return a value.
How can I change my code so that it returns immediately when I have found a path that works for this problem instead of going through all the recursive calls that I have made previously
One particularly straightforward way is to throw an exception, which will immediately unwind the stack.
def can_jump(nums: list[int]) -> bool:
if not nums:
return False
class _Success(Exception):
pass
def backtrack(i):
if i >= len(nums):
return
if i == len(nums) - 1:
raise _Success()
for x in range(1, nums[i] + 1):
backtrack(i + x)
try:
backtrack(0)
return False
except _Success:
return True
We create a local exception type called _Success that the backtracking search can throw to indicate that it found a solution.
If it never finds a solution, the backtrack function will simply return and the return False line will run. Otherwise, it will raise _Success() and then the return True line will run.

how to check if a list is a consecutive number in python?

How to write python code that let the computer know if the list is a right sequence and the position doesn't matter, it will return true, otherwise it return false.
below are some of my example, I really don't know how to start
b=[1,2,3,4,5] #return true
b=[1,2,2,1,3] # return false
b=[2,3,1,5,4] #return true
b=[2,4,6,4,3] # return false
sort function is O(nlogn), we can use for loop which is O(n):
def check_seq(in_list):
now_ele = set()
min_ele = max_ele = in_list[0]
for i in in_list:
if i in now_ele:
return False
min_ele = min(i, min_ele)
max_ele = max(i, max_ele)
now_ele.add(i)
if max_ele-min_ele+1 == len(in_list):
return True
return False
Create a set and one to compare with -- based on minimum and maximum:
isRightSequence = set(range(min(b), max(b)+1)) == set(b)
This question is quite simple and can be solved a few ways.
The conditional approach - if there is a number that is bigger than the length of the list, it automatically cannot be a sequence because there can only be numbers from 1-n where n is the size of the list. Also, you have to check if there are any duplicates in the list as this cannot be possible either. If none of these conditions occur, it should return true
Using dictionary - go through the entire list and add it as a key to a dictionary. Afterwards, simply loop through numbers 1-n where n is the length of the list and check if they are keys in the dictionary, if one of them isn't, return false. If all of them are, return true.
Both of these are quite simply approaches and you should be able to implement them yourselves. However, this is one implementation for both.
1.
def solve(list1):
seen = {}
for i in list1:
if i > len(list1):
return False
if i in seen:
return False
seen[i] = True
return False
def solve(list1):
seen = {}
for i in list1:
seen[i] = True
for i in range (1, len(list1)+1):
if i not in seen:
return False
return True
This solution needs O(n) runtime and O(n) space
def is_consecutive(l: list[int]):
if not l:
return False
low = min(l)
high = max(l)
# Bounds Check
if high - low != len(l) - 1:
return False
# Test all indices exist
test_vec = [False] * len(l) # O(n)
for i in range(len(l)):
test_vec[l[i] - low] = True
return all(test_vec)
assert is_consecutive(range(10))
assert is_consecutive([-1, 1,0])
assert not is_consecutive([1,1])
assert not is_consecutive([1,2,4,6,5])

Finding cycle in graph using DFS - recursion problem

I tried to make an aglorithm that searches for cycles in undirected graph but it seems like return statement doesn't work.
def wrapped_dfs_recursive(G: Dict[int, List[int]], current_vertex: int, parent: int, visited: List[int] = None):
for neighbour in G[current_vertex]:
if neighbour in visited and neighbour != parent:
print("Found cycle")
return True
visited.append(current_vertex)
if current_vertex in G.keys():
for vertex in G[current_vertex]:
if vertex not in visited:
wrapped_dfs_recursive(G, current_vertex=vertex, parent=current_vertex, visited=visited)
else:
return None
return False
When I execute this code for a simple, cyclic graph it returns False, but it also prints the message "Found cycle" two times. So if it executes the print function why doesn't it return True? Did I make something wrong with the stop condition?
Ok, I found the issue. I missed the word 'return' when I was calling wrapped_dfs_recursive(), so it wasn't changing anything and finally returned False.

Bfs modification to find compatible paths in a directed graph

I have the following directed graph and every node has one or more attributes. I try to modify bfs algorithm to find all possible paths from a starting node until the given attributes are covered. I also want the path that i found to not be a part of a cycle.
For this graph if i start from node 1 and i want to cover attr 4 the paths that my algorithm will find are:
1-2-3
1-2-5-3
1-2-5-6-8
If i add the edge 3-1 then the paths 1-2-3 and 1-2-5-3 i want not to be accepted because are part of a cycle. So in my algorithm i try to check the neighbors of the last visited node and if the neighbor has already visited then i try to discard this path but my algorithm doesnt work in this case. If i add the edge 3-1 it returns the same paths. How can i fix this?
Here is my code:
G = nx.DiGraph()
G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(2,5)
G.add_edge(3,4)
G.add_edge(5,3)
G.add_edge(5,6)
G.add_edge(5,7)
G.add_edge(6,8)
G.add_edge(3,1)
def checkIfRequiredAttrsAreCovered(path, attrsToBeCovered):
coveredAttrs = []
counter = 0
for node in path:
coveredAttrs.extend(G.node[node]['attrs'])
for i in attrsToBeCovered:
if i in coveredAttrs:
counter = counter + 1
if counter == len(attrsToBeCovered):
return True
else:
return False
def bfs(G, startingNode, attrsToBeCovered):
paths = []
q = queue.Queue()
q.put([startingNode])
while not q.empty():
v = q.get()
if checkIfRequiredAttrsAreCovered(v, attrsToBeCovered) == True:
for i in G.neighbors(v[-1]):
if i in v:
break
paths.append(v) #print(v)
else:
for node in G.neighbors(v[-1]):
if node not in v:
path = []
path.extend(v)
path.append(node)
q.put(path)
print(paths)
I'll assume that you don't care if nodes are part of a bigger cycle. E.g. if 4 is connected to 1 and 3 is in a cycle 1-2-3-4. If you want to handle this, you may start a dfs from each matching node, with the current path set as visited.
First, you should use snake case in Python
Second, you should use sets to compare the attributes covered to the attributes to be covered. For a path, compute the set of covered attributes and compare the sets:
def check_if_required_attrs_are_covered(G, path, attrs_to_be_covered): # be sure to pass G as an argument here
covered_attrs = set([G.node[n]['attrs'] for n in path])
return covered_attrs >= attrs_to_be_covered
Third, some remarks on the bfs function:
A test if b == True: is equivalent to if b:, because for a boolean b == (b == True) (try with True and False to convince yourself)
The way you append a path to q may be shortened to q.put(v+ [node])
You probably do not need a synchonized queue: use a list
Use return instead of print or even better, create a generator that yields paths when they are found.
Four: what is the problem? Look at the for i in G.neighbors(v[-1]): loop.
Whether you break or not, you go to the line paths.append(v).
That's why you do not exclude the paths with cycles. You want to distinguish the normal end of the loop from the break.
That's a perfect case for confidential loop syntax in Python: the for...else loop.
I quote the doc: "a loop’s else clause runs when no break occurs". This gives the following code:
for i in G.neighbors(v[-1]):
if i in v:
break
else: # no neighbor from v[-1] in v
yield v # instead of paths.append(v)
But you can also use any for a more natural expression:
if not any(i in v for i in G.neighbors(v[-1])):
yield v # instead of paths.append(v)
This gives the following code:
def bfs(G, starting_node, attrs_to_be_covered):
q = [[starting_node]]
while q:
v = q.pop()
if check_if_required_attrs_are_covered(G, v, attrs_to_be_covered): # be sure to pass G as an argument
if not any(i in v for i in G.neighbors(v[-1])):
yield v
else:
for node in G.neighbors(v[-1]):
if node not in v:
q.append(v+ [node])
Try it with:
print (list(bfs(G, 1, set(["attr4"]))))

Python recursion - how to exit early

I've been playing with BST (binary search tree) and I'm wondering how to do an early exit. Following is the code I've written to find kth smallest. It recursively calls the child node's find_smallest_at_k, stack is just a list passed into the function to add all the elements in inorder. Currently this solution walks all the nodes inorder and then I have to select the kth item from "stack" outside this function.
def find_smallest_at_k(self, k, stack, i):
if self is None:
return i
if (self.left is not None):
i = self.left.find_smallest_at_k(k, stack, i)
print(stack, i)
stack.insert(i, self.data)
i += 1
if i == k:
print(stack[k - 1])
print "Returning"
if (self.right is not None):
i = self.right.find_smallest_at_k(k, stack, i)
return i
It's called like this,
our_stack = []
self.root.find_smallest_at_k(k, our_stack, 0)
return our_stack[k-1]
I'm not sure if it's possible to exit early from that function. If my k is say 1, I don't really have to walk all the nodes then find the first element. It also doesn't feel right to pass list from outside function - feels like passing pointers to a function in C. Could anyone suggest better alternatives than what I've done so far?
Passing list as arguments: Passing the list as argument can be good practice, if you make your function tail-recursive. Otherwise it's pointless. With BST where there are two potential recursive function calls to be done, it's a bit of a tall ask.
Else you can just return the list. I don't see the necessity of variable i. Anyway if you absolutely need to return multiples values, you can always use tuples like this return i, stack and this i, stack = root.find_smallest_at_k(k).
Fast-forwarding: For the fast-forwarding, note the right nodes of a BST parent node are always bigger than the parent. Thus if you descend the tree always on the right children, you'll end up with a growing sequence of values. Thus the first k values of that sequence are necessarily the smallest, so it's pointless to go right k times or more in a sequence.
Even in the middle of you descend you go left at times, it's pointless to go more than k times on the right. The BST properties ensures that if you go right, ALL subsequent numbers below in the hierarchy will be greater than the parent. Thus going right k times or more is useless.
Code: Here is a pseudo-python code quickly made. It's not tested.
def findKSmallest( self, k, rightSteps=0 ):
if rightSteps >= k: #We went right more than k times
return []
leftSmallest = self.left.findKSmallest( k, rightSteps ) if self.left != None else []
rightSmallest = self.right.findKSmallest( k, rightSteps + 1 ) if self.right != None else []
mySmallest = sorted( leftSmallest + [self.data] + rightSmallest )
return mySmallest[:k]
EDIT The other version, following my comment.
def findKSmallest( self, k ):
if k == 0:
return []
leftSmallest = self.left.findKSmallest( k ) if self.left != None else []
rightSmallest = self.right.findKSmallest( k - 1 ) if self.right != None else []
mySmallest = sorted( leftSmallest + [self.data] + rightSmallest )
return mySmallest[:k]
Note that if k==1, this is indeed the search of the smallest element. Any move to the right, will immediately returns [], which contributes to nothing.
As said Lærne, you have to care about turning your function into a tail-recursive one; then you may be interested by using a continuation-passing style. Thus your function could be able to call either itself or the "escape" function. I wrote a module called tco for optimizing tail-calls; see https://github.com/baruchel/tco
Hope it can help.
Here is another approach: it doesn't exit recursion early, instead it prevents additional function calls if not needed, which is essentially what you're trying to achieve.
class Node:
def __init__(self, v):
self.v = v
self.left = None
self.right = None
def find_smallest_at_k(root, k):
res = [None]
count = [k]
def helper(root):
if root is None:
return
helper(root.left)
count[0] -= 1
if count[0] == 0:
print("found it!")
res[0] = root
return
if count[0] > 0:
print("visiting right")
find(root.right)
helper(root)
return res[0].v
If you want to exit as soon as earlier possible, then use exit(0).
This will make your task easy!

Categories