Converting recursive algorithm to iterative with stacks - algorithm

My professor gave this exercise to us in class and asked to exactly mimic the recursive calls with stacks. This is the recursive algorithm:
def algo(T, h):
ret = -1
s = 0
d = 0
if T != None:
if T.left != None:
s = algo(T.left, h + 1)
if T.right != None:
d = algo(T.right, h + 1)
if s == 0 and d == 0:
ret = h
else:
ret = s + d
return ret
This is my attempt at solving this exercise (I used two stacks, one for saving T and another one to save "s")
def algo_it(T, h):
ret = -1
s = 0
d = 0
curr = T
next = None
last = None
stack_T = Stack()
stack_S = Stack()
while curr != None or not stack_T.empty():
if curr != None:
# First time entering the function
ret = -1
s = 0
d = 0
if curr.left != None:
# Left recursive call
h = h + 1
next = curr.left
# Save current T because it will be popped when going up
# on recursion tree
stack_T.push(curr)
elif curr.right != None:
# Right recursive call
h = h + 1
next = curr.right
stack_T.push(curr)
else:
# Force going up on recursion tree
next = None
else:
# Coming up from recursion tree
curr = stack_T.top()
if last != curr.right and curr.right != None:
# We're here from the 1st recursive call (left)
# We have to go in right recursive call now
h = h + 1
next = curr.right
# We are returning from left, so ret = s = algo(T.left, h + 1)
s = ret
stack_S.push(s)
ret = -1
d = 0
else:
stack_T.pop()
if curr.right != None:
s = stack_S.pop()
d = ret
else:
s = ret
d = 0
if s == 0 and d == 0:
ret = h
else:
ret = s + d
last = curr
curr = next
return ret
The algorithm is failing because on stack_S.pop() I'm popping when stack is empty so I get runtime error. Am I close to get a solution?
I highly appreciate all of your help!

When you are mimicking the recursive call, you are only pushing on the stack that you know you are supposed to return to. But you aren't pushing the context to know which one to push the response to.
I would suggest that you do this problem in multiple steps.
Convert all of your function local variables (T, h, ret, s and d) into stacks outside of your recursive function, with explicit pop/push instead of declaring them, and expecting them to disappear when your function ends. (Hint: leave ret on the stack, and pop off of it as you manipulate stack_s or stack_d.) This may require creating a helper function for the very first recursive call.
Put labels in your function for where you first call it, and every place that you can return.
Convert to iterative as follows. Add a stack for the function calls. It will contain a label for where to be in the function. Instead of making a recursive call you change the top of the function stack to say where to return to in this call, and push a new one on to say start a new function, and then go to the next loop iteration. (The condition for exiting the loop is that the function stack is empty, and your answer will be the top of the ret stack.)
This is a somewhat lengthy but mechanical process. It should give you an appreciation for how much the computer just does for you normally. :-)

Related

How to tell which of two glob patterns is more general

Let's say we have two glob patterns, for example app/src/**/* and app/src/some-dir/**/*.
Both patterns match some path, for example app/src/some-dir/some-other-dir/my-file.txt.
I need to tell, which of this two (or more) patterns is more general. In other words, which of patterns is a subset of another pattern. In example above more general pattern is app/src/**/*, because it matches everything in app/src, and second pattern matches everything in a subdirectory of app/src.
The first thought is to tell, which path prefix is longer (part of pattern before any special symbols like *), but I believe that's not a solution to a problem, since pattern can be more complicated and can include special characters in different places, something like app/*/some-dir/some-other-dir.
Is there any reliable solution for this problem, that does not require actual globbing and counting matched files (since it may be a slow operation) ?
The textbook method for this is to convert each glob to a deterministic finite automaton, then construct the product of the automata (implicitly here), and search for a state that is accepting in one but not the other. Depending on how many subset checks you're planning to do and how big the state machines get, it may be worth adding a DFA minimization stage (also covered in your favorite automata theory textbook).
Some rough Python 3:
import re
def glob_tokens_from_glob(g):
return re.findall(r"\*\*?|[^*]", g)
def is_wild(token):
return token.startswith("*") or token == "?"
def epsilon_successors(tokens, i):
yield i
while i < len(tokens) and tokens[i].startswith("*"):
i += 1
yield i
def successors(tokens, i, sym):
if i >= len(tokens):
pass
elif tokens[i] == "**":
yield i
elif tokens[i] == "*":
if sym != "/":
yield i
elif tokens[i] == "?":
if sym != "/":
yield i + 1
elif tokens[i] == sym:
yield i + 1
def successor_dict(tokens, q):
symbols = {tokens[i] for i in q if i < len(tokens) if not is_wild(tokens[i])}
symbols.update({"/", "[^/]"})
return {
sym: frozenset(
k
for i in q
for j in successors(tokens, i, sym)
for k in epsilon_successors(tokens, j)
)
for sym in symbols
}
def dfa_from_glob_tokens(tokens):
q0 = frozenset(epsilon_successors(tokens, 0))
delta = {frozenset(): {"[^/]": frozenset()}}
stack = [q0]
while stack:
q = stack.pop()
if q in delta:
continue
d = successor_dict(tokens, q)
stack.extend(d.values())
delta[q] = d
return (q0, delta, {q for q in delta.keys() if len(tokens) in q})
def dfa_from_glob(g):
return dfa_from_glob_tokens(glob_tokens_from_glob(g))
def successor(d, sym):
if sym in d:
return d[sym]
elif sym == "/":
return frozenset()
else:
return d["[^/]"]
def dfa_matches_subset(dfa_a, dfa_b):
q0_a, delta_a, f_a = dfa_a
q0_b, delta_b, f_b = dfa_b
stack = [(q0_a, q0_b)]
visited = set()
while stack:
q = stack.pop()
if q in visited:
continue
visited.add(q)
q_a, q_b = q
if q_a in f_a and q_b not in f_b:
return False
d_a = delta_a[q_a]
d_b = delta_b[q_b]
symbols = set(d_a.keys())
symbols.update(d_b.keys())
stack.extend((successor(d_a, sym), successor(d_b, sym)) for sym in symbols)
return True
def test():
dfa1 = dfa_from_glob("app/src/**/*")
dfa2 = dfa_from_glob("app/src/some-dir/**/*")
dfa3 = dfa_from_glob("app/src/some-dir/some-other-dir/my-file.txt")
dfa4 = dfa_from_glob("app/*/some-dir/some-other-dir/*")
dfa5 = dfa_from_glob("*")
dfa6 = dfa_from_glob("/")
dfa7 = dfa_from_glob("?")
dfa8 = dfa_from_glob("b")
dfa9 = dfa_from_glob("cc")
dfas = [dfa1, dfa2, dfa3, dfa4, dfa5, dfa6, dfa7, dfa8, dfa9]
for a in dfas:
for b in dfas:
print(int(dfa_matches_subset(a, b)), end=" ")
print()
test()

Is there any way to use the return value as an argument to the same function during the previous recursion in merge sort

I am coding the Merge sort algorithm but somehow got stuck with a problem. The problem is that I need to use the return value of the merge function as an argument as an previous recursive call of the same merge function. Sorry for not being clear.
Here is my code:
a = [10,5,2,20,-50,30]
def mergeSort(arr):
l = 0
h = len(arr)-1
if h > l:
mid = (l+h) // 2
left = arr[l:mid+1]
right = arr[mid+1:]
mergeSort(left)
mergeSort(right)
merge(left, right)
def merge(l, r):
subarr = []
lc = 0
rc = 0
loop = True
while loop:
if lc > len(l)-1 and rc <= len(r)-1:
for i in range(rc, len(r)):
subarr.append(r[i])
loop = False
elif lc <= len(l)-1 and rc > len(r)-1:
for i in range(lc, len(l)):
subarr.append(l[i])
loop = False
elif l[lc] < r[rc]:
subarr.append(l[lc])
lc += 1
loop = True
elif r[rc] < l[lc]:
subarr.append(r[rc])
rc += 1
loop = True
elif l[lc] == r[rc]:
subarr.append(l[lc])
subarr.append(r[rc])
lc += 1
rc += 1
loop = True
mergeSort(a)
Any help will be appreciated thank you :)
First you need to actually return the result. Right now you return nothing so get None back.
Secondly, just assign to the same variable. left = mergeSort(left) and so on.
UPDATE:
Here is a debugged version.
a=[10,5,2,20,-50,30]
def mergeSort(arr):
l=0
h=len(arr)-1
if h>l:
mid=(l+h)//2
left=arr[l:mid+1]
right=arr[mid+1:]
# Capture the merge into variables here.
left=mergeSort(left)
right=mergeSort(right)
# Need a return of the merge.
return merge(left,right)
# Need to return arr if arr has 0 or 1 elements.
else:
return arr
def merge(l,r):
subarr=[]
lc=0
rc=0
loop=True
while loop:
if lc>len(l)-1 and rc<=len(r)-1:
for i in range(rc,len(r)):
subarr.append(r[i])
loop=False
elif lc<=len(l)-1 and rc>len(r)-1:
for i in range(lc,len(l)):
subarr.append(l[i])
loop=False
elif l[lc]<r[rc]:
subarr.append(l[lc])
lc+=1
loop=True
elif r[rc]<l[lc]:
subarr.append(r[rc])
rc+=1
loop=True
elif l[lc]==r[rc]:
subarr.append(l[lc])
subarr.append(r[rc])
lc+=1
rc+=1
loop=True
# Need to return the results of merge.
return subarr
# Need to actually try calling the function to see the result.
print(mergeSort(a))
I also indented more sanely. Trust me, it matters.
There are multiple problems in our code:
you do not return the sorted slice from mergeSort nor merge. Your implementation does not sort the array in place, so you must return subarr in merge and the return value of merge in mergeSort or arr if the length is less than 2.
your code is too complicated: there are many adjustments such as mid+1, len(l)-1, etc. It is highly recommended to use index values running from 0 to len(arr) excluded. This way you do not have to add error prone +1/-1 adjustments.
the merge function should proceed in 3 phases: merge the left and right arrays as long as both index values are less than the array lengths, then append remaining elements from the left array, finally append remaining elements from the right array.
there is no need to make 3 different tests to determine from which of the left and right array to take the next element, a single test is sufficient.
also use a consistent amount of white space to indent the blocks, 3 or 4 spaces are preferable, tabs are error prone as they expand to different amount of white space on different devices, mixing tabs and spaces, as you did is definitely a problem.
Here is a modified version:
def mergeSort(arr):
# no need to use l and h, use len(arr) directly
if len(arr) > 1:
# locate the middle point
mid = len(arr) // 2
# left has the elements before mid
left = arr[:mid]
# right has the elements from mid to the end
right = arr[mid:]
# sort the slices
left = mergeSort(left)
right = mergeSort(right)
# merge the slices into a new array and return it
return merge(left, right)
else:
# return the original array (should actually return a copy)
return arr
def merge(l, r):
subarr = []
lc = 0
rc = 0
# phase1: merge the arrays
while lc < len(l) and rc < len(r):
if l[lc] <= r[rc]:
subarr.append(l[lc])
lc += 1
else:
subarr.append(r[rc])
rc += 1
# phase2: copy remaining elements from l
while lc < len(l):
subarr.append(l[lc])
lc += 1
# phase3: copy remaining elements from r
while rc < len(r):
subarr.append(r[rc])
rc += 1
# return the merged array
return subarr
a = [10, 5, 2, 20, -50, 30]
print(mergeSort(a))

QuickSort - Median Three

I am working on the QuickSort - Median Three Algorithm.
I have no problem with the first and last element sorting. But, when comes to the Median-three, I am slightly confused. I hope someone could help me on this.
Would be appreciate if someone could provide me some pseudocode?
My understanding is to get the middle index by doing this. (start + end) / 2 , then swap the middle pivot value to the first value, after all these done it should goes well with the normal quick sort ( partitioning and sorting).
Somehow, I couldn't get it works. Please help!
#Array Swap function
def swap(A,i,k):
temp=A[i]
A[i]=A[k]
A[k]=temp
# Get Middle pivot function
def middle(lista):
if len(lista) % 2 == 0:
result= len(lista) // 2 - 1
else:
result = len(lista) // 2
return result
def median(lista):
if len(lista) % 2 == 0:
return sorted(lista)[len(lista) // 2 - 1]
else:
return sorted(lista)[len(lista) // 2]
# Create partition function
def partition(A,start,end):
m = middle(A[start:end+1])
medianThree = [ A[start], A[m], A[end] ]
if A[start] == median(medianThree):
pivot_pos = start
elif A[m] == median(medianThree):
tempList = A[start:end+1]
pivot_pos = middle(A[start:end+1])
swap(A,start,pivot_pos+start)
elif A[end] == median(medianThree):
pivot_pos = end
#pivot = A[pivot_pos]
pivot = pivot_pos
# swap(A,start,end) // This line of code is to switch the first and last element pivot
swap(A,pivot,end)
p = A[pivot]
i = pivot + 1
for j in range(pivot+1,end+1):
if A[j] < p:
swap(A,i,j)
i+=1
swap(A,start,i-1)
return i-1
count = 0
#Quick sort algorithm
def quickSort(A,start,end):
global tot_comparisons
if start < end:
# This to create the partition based on the
pivot_pos = partition(A,start,end)
tot_comparisons += len(A[start:pivot_pos-1]) + len(A[pivot_pos+1:end])
# This to sort the the left partition
quickSort(A,start,pivot_pos -1)
#This to sort the right partition
quickSort(A,pivot_pos+1,end)

Algorithms, DFS

I've written a program to find shortest path in a N*N grid recursively.
def dfs(x,y,Map,p):
N = len(Map)
p += [[x,y]]
if Map[x][y] == 'E':
return p
for i in [[x-1,y],[x+1,y],[x,y-1],[x,y+1]]:
if N > i[0] >= 0 and N > i[1] >= 0 :
if (Map[i[0]][i[1]] == 'P' or Map[i[0]][i[1]] == 'E') and i not in p:
dfs(i[0], i[1], Map,p)
return []
When Map[x][y] = 'E' the recursion don't stop and return p. But it goes till the end. How to correct it and return the path(p).
By the looks of it, the code is prone to loop indefinitely. This is due to lack of checks whether you've entered a node before and moving in all (4) directions from a given node.
To solve it simply, add another array NxN of Boolean values answering the question: visited?. Then update the code to something along the lines:
def dfs(x,y,Map,visited,p):
visited[x,y] = true;
N = len(Map)
(...)
if (Map[i[0]][i[1]] == 'P' or Map[i[0]][i[1]] == 'E')
and i not in p
and visited[i[0], i[1]] == false:
dfs(i[0], i[1], Map,visited,p)

How do I improve the algorithm for my Traffic Jam recursive solver?

Theres a cute little game on Android called Traffic Jam
I've written a recursive solver:
import copy,sys
sys.setrecursionlimit(10000)
def lookup_car(car_string,ingrid):
car=[]
i=0
for row in ingrid:
j=0
for cell in row:
if cell == car_string:
car.append([i,j])
j+=1
i+=1
return car
#True if up/down False if side to side
def isDirectionUp(car):
init_row=car[0][0]
for node in car:
if node[0] != init_row:
return True
return False
def space_up(car,grid):
top_node=car[0]
m_space_up = (top_node[0]-1,top_node[1])
if top_node[0] == 0:
return -1
elif grid[m_space_up[0]][m_space_up[1]] != " ":
return -1
else:
return m_space_up
def space_down(car,grid):
bottom_node = car[-1]
m_space_down = (bottom_node[0]+1,bottom_node[1])
if bottom_node[0] == 5 :
return -1
elif grid[m_space_down[0]][m_space_down[1]] != " ":
return -1
else:
return m_space_down
def space_left(car,grid):
left_node = car[0]
m_space_left = (left_node[0],left_node[1]-1)
if left_node[1] == 0 :
return -1
elif grid[m_space_left[0]][m_space_left[1]] != " ":
return -1
else:
return m_space_left
def space_right(car,grid):
right_node = car[-1]
m_space_right = (right_node[0],right_node[1]+1)
if right_node[1] == 5 :
return -1
elif grid[m_space_right[0]][m_space_right[1]] != " ":
return -1
else:
return m_space_right
def list_moves(car,grid):
ret =[]
if isDirectionUp(car):
up = space_up(car,grid)
if up != -1:
ret.append(("UP",up))
down = space_down(car,grid)
if down != -1:
ret.append(("DOWN",down))
else:
left = space_left(car,grid)
if left != -1:
ret.append(("LEFT",left))
right = space_right(car,grid)
if right != -1:
ret.append(("RIGHT",right))
return ret
def move_car(car_string,move,ingrid):
grid = copy.deepcopy(ingrid)
car = lookup_car(car_string,grid)
move_to = move[1]
front = car[0]
back = car[-1]
if(move[0] == "UP" or move[0] == "LEFT"):
grid[back[0]][back[1]] = " "
grid[move_to[0]][move_to[1]] = car_string
elif(move[0] == "DOWN" or move[0] == "RIGHT"):
grid[front[0]][front[1]] = " "
grid[move_to[0]][move_to[1]] = car_string
return grid
def is_solution(grid):
car = lookup_car("z",grid)
if(car[-1] == [2,5]):
return True
elif space_right(car,grid) == -1:
return False
else:
solgrid = move_car("z",("RIGHT",space_right(car,grid)),grid)
return is_solution(solgrid)
def print_grid(grid):
for row in grid:
print ''.join(row)
def solve(grid,solution,depth):
global stop
global state
if grid in state:
return
else:
state.append(grid)
if stop:
return
if is_solution(grid):
print_grid(grid)
print len(solution)
else:
for each in "abcdefzhijklm":
car = lookup_car(each,grid)
moves = list_moves(car,grid)
for move in moves:
solution.append((each,move))
moved_grid = move_car(each,move,grid)
solve(moved_grid,solution,depth)
stop=False
state=[]
recdepth=0
#grid file using a-w and x means free space, and ' ' means yellow car
grid=[list(x) for x in file(sys.argv[1]).read().split('\n')[0:-1]]
solve(grid,[],0)
WHERE grid is in a file:
abccdd
abeef
azzhfi
jjjhfi
kll
kmm
But, it takes over 8000 moves to find a solution ,and fails to find a simple 30 move solution. What is wrong with the algorithm?
If the branching factor of your search space is r then the number of vertices in the search tree to depth n is (1-r^n)/(1-r). A problem with a minimal 30 step solution, even in the simple case where r = 2, will have a search tree with around 2^30 - 1 ~= 1,000,000,000 vertices. Now, your branching factor is likely to be bigger than 2, so a 30 step problem is a long way from trivial!
That said, I'd be inclined to (a) find a better representation of your problem (searching arrays of strings is slow) and (b) consider best-first search where you guide your search with a heuristic (e.g., distance of the yellow car from its destination or the number of cars blocking the path of the yellow car).
Hope this helps.
This is essentially a (relatively token) searching problem, with a huge search space. As others recommended, read up on Depth-first search, and then read up on Breadth-first search, and when you learn the difference, read up on A* Search, and coming up with a pessimistic scoring function.
Also, note that in this case, you already know what the end state should be, thus, another approach would be to search from both ends and meet in the middle. This should reduce your search space considerably.
If that's still not enough, you can combine the techniques!

Resources