Breadth search algorithm implementation - python

I wrote a python code for Breadth search algorithm for 8 puzzle probelm. The states of the game are coded as a list of lists (two dimensional list). The goal is to reach [[1,2,3],[8,0,4],[7,6,5]] from a given state. The code output the path from a given state to the goal. It works fine but for this particular case [[0,1,2],[7,8,3],[6,5,4]] does not give the whole path rather it gives the last three states in the path.
Would you please try to point out the place of the bugs. Here is the code. Note The program starts from the statement:
"""
This program solves the 8 puzzel problems using Breadth First Search Algorithm.
"""
import copy
def successor(astate):
"""
This function takes an instance of the 8 puzzel problem and generates all the legal successors.
"""
up = copy.deepcopy(astate)
"""
important note:
I use [:] and list() to copy lists but it was not working fine, hence deecopy is used instead.
"""
down = copy.deepcopy(astate)
left = copy.deepcopy(astate)
right = copy.deepcopy(astate)
successors = []
for i in range(3):
for j in range(3):
if astate[i][j] == 0:
row = i
col = j
if row != 0:
dummy = up[row][col]
up[row][col] = up[row -1][col]
up[row-1][col] = dummy
successors.append(up)
if row != 2:
dummy = down[row][col]
down[row][col] = down[row+1][col]
down[row+1][col] = dummy
successors.append(down)
if col != 2:
dummy = right[row][col]
right[row][col] = right[row][col+1]
right[row][col+1] = dummy
successors.append(right)
if col != 0:
dummy = left[row][col]
left[row][col] = left[row][col-1]
left[row][col-1] = dummy
successors.append(left)
return successors
def puzzle(astate):
"""
This function takes a given instance of the 8 puzzel problem and returns the path to the goal where the goal is defined as below.
"""
goal = [[1,2,3],[8,0,4],[7,6,5]] #The goal state.
generation = [astate] #Nodes generated at each level.
tracking = {} #Track the path to the goal.
path = [] #The path from the root to the goal.
parent = generation.pop(0) #Takes the first element of the list!!
successors = successor(parent) #Generate successors.
key = str(parent) #keys odictionaries must be hashable and mutable. Lists are illegal keys.
tracking[key] = successors #Associate successors with their parent.
for asuccessor in successors:
generation.append(asuccessor) #Generate the first level.
if goal in generation: #if the goal is among the successors returns the path to it.
path.insert(0, key)
path.insert(0, goal)
return path
else:
while generation != []: #keep searching!
parent = generation.pop(0)
successors = successor(parent) #generate successors
key = str(parent)
tracking[key] = successors
if goal in [astate for astate in successors]: #if the goal is among the successors backtrack its path.
path.insert(0, str(goal)) #Just because path contains states as strings!!
path.insert(0, key)
for key in tracking.keys():
for value in tracking.get(key):
if str(parent) == str(value): #If the current (parent) is among the values of (key) then (key) is its parent.
path.insert(0, key)
parent = key
break
return path
else: #keep searching
for asuccessor in successors:
if asuccessor not in generation: #If the current successors is already generated do not add it.
if str(asuccessor) not in tracking.keys(): #If the successor is a previous parent do not add it.
generation.append(asuccessor)
return

Related

How to index each node in O(1) complexity and update all indexes each time when doing insertion/deletion/rotations in a Balanced AVL tree

The solution I have now is something of an in order traversal:
def update_index(self, current: AVLTreeNode) -> None:
"""
Call updated_index_aux to traverse the tree in order and update the index appropriately
"""
# if current is not None:
# return current.index
# return 0
self.index = 0
self.update_index_aux(current)
def update_index_aux(self, current: AVLTreeNode) -> None:
if current is not None: #Base case if current is None
self.update_index_aux(current.left)
current.index = self.index
self.index += 1
self.update_index_aux(current.right)
but this requires me to call this method each time i insert/delete which is not efficient, how do i make it O(1) such that everything is updated each time i insert/delete or perform a rotation. It seems similar to get_height() method in an AVL tree which is O(1) so this is called on insert and delete:
# Update height of current node
current.height = max(self.get_height(current.left),
self.get_height(current.right)) + 1
but im not sure how to take these solutions to make it O(1) so i dont have to traverse the tree each time i insert or delete.
my get_height function:
def get_height(self, current: AVLTreeNode) -> int:
if current is not None:
return current.height
return 0
Below is my insert function into a AVL tree for reference:
def insert_aux(self, current: AVLTreeNode, key: K, item: I) -> AVLTreeNode:
"""
Attempts to insert an item into the tree, it uses the Key to insert it
"""
if current is None: # base case: at the leaf
current = AVLTreeNode(key, item)
self.length += 1
elif key < current.key:
current.left = self.insert_aux(current.left, key, item)
elif key > current.key:
current.right = self.insert_aux(current.right, key, item)
else: # key == current.key
raise ValueError('Inserting duplicate item')
current.height = max(self.get_height(current.left), self.get_height(current.right)) + 1 #Update height of current node
current = self.rebalance(current) #Rebalance tree after insertion if needed
return current
The reason i need to index each node is because I need to code a function that finds and gets a range of items stored in the AVL tree based on a range of indexes. I did that part already but I can only do it when the nodes are already indexed and the only way i can think of to index the nodes is in order traversal to each node and updating the index accordingly but I cant do it this way so Im trying to think of a way to update each index during insertion/deletion/rotations without needing to traverse each node and update

Returning shortest path using breadth-first search

I have a graph:
graph = {}
graph['you'] = ['alice', 'bob', 'claire']
graph['bob'] = ['anuj', 'peggy']
graph['alice'] = ['peggy']
graph['claire'] = ['thom', 'jonny']
graph['anuj'] = []
graph['peggy'] = []
graph['thom'] = []
graph['jonny'] = []
A function that determines my end node:
def person_is_seller(name):
return name[-1] == 'm' # thom is the mango seller
And a breadth-first search algorithm:
from collections import deque
def search(name):
search_queue = deque()
search_queue += graph[name]
searched = set()
while search_queue:
# print(search_queue)
person = search_queue.popleft()
if person not in searched:
# print(f'Searching {person}')
if person_is_seller(person):
return f'{person} is a mango seller'
else:
search_queue += graph[person]
searched.add(person)
return f'None is a mango seller'
search('you')
# 'thom is a mango seller'
I am wondering if this algorithm can return the shortest path from you to thom?
[you, claire, thom] # as this is the shortest path to thom which is my end node
I checked this answer and it states that it does not let me find the shortest path but the second answer states that it is possible to provide the shortest path, I assume without using Djikstra's algorithm. So I am a bit confused, can I somehow keep track of the previous node, and if the final node is reached provide the shortest path as in the last code snippet or in any other format?
You can make searched a dictionary instead of a set, and then let the value for each key be a backreference to the node where you came from.
When you find the target, you can recover the path by walking back over those backreferences and then return the reverse of that.
Adapted code:
def search(name):
search_queue = deque()
search_queue.append((name, None))
searched = {} # dict instead of set
while search_queue:
# print(search_queue)
person, prev = search_queue.popleft()
if person not in searched:
searched[person] = prev
# print(f'Searching {person}')
if person_is_seller(person):
result = []
while person is not None:
result.append(person)
person = searched[person]
return result[::-1]
else:
search_queue += [(neighbor, person) for neighbor in graph[person]]
return []
Now the function returns a list. It will have the start and end node when a path is found, so in this case:
['you', 'claire', 'thom']
If no path is found, the result is an empty list.
You can use BFS to find the shortest path provided that every edge has the same length. Dykstra's algorithm is necessary when different edges have different weights.
Dykstra's algorithm in its pure form only computes the length of the shortest path. Since you probably want the shortest path itself, you'll want to associate each visited node with the node on the other end of the edge, which is generally done using an associative array (a "dictionary", in Python).

Linked list with two pointers trying to catch a loop

Hello I need help on detecting a loop and returning False on a linked list but first, let me explain how this linked list looks like:
This would be the node class:
class Node:
def __init__(self, next = None, stairs = None):
self.next = next
self.stairs = stairs
Since your step size doesn't change the code will, once it has visited a node twice visit it over and over again. This is due to the fact that it will make the same decisions at every node that follows the repeated node. Thus you only have to detect if (at least) one node has been visited twice. Two common ways of doing this are the following:
Count how many nodes there are in the linked list (i.e. take a new variable N=0 and increment it on every run of the first loop of the play function). Then in the second loop count how many nodes have been visited. Once that number is larger than N you know that at least one node has been visited at least twice and therefore you have detected a circle and need to break out of the loop (or return).
def play(first, step):
'''(LinkedList, int) -> bool
'''
# locates the end_node
end_list = first
found = False
# Used to find Node population
N = 0
while end_list.next != None and found != True:
if end_list.next == first:
found = True
else:
end_list = end_list.next
N = N + 1
stepcount = 1
count = 1
current = first
winner = False
loop = False
# Goes through the linked list
while current != None and winner != True and loop != True:
# If count == step, then we check if we are at the last square or
# else if we are on a stairs then we may use them.
# If none of them are found then we itterate normally making count back to 1.
# If stepcount passes the overall N (population of all the nodes), then it will detect a loop
if stepcount > N:
loop = True #one node has been visited more than once so there is a loop
elif count == step:
if current == end_list:
winner = True
elif current.stairs:
current = current.stairs
count = 0
else:
current = current.next
count = 0
stepcount = stepcount + 1
else:
current = current.next
count = count + 1
return current == end_list
Keep track of which nodes you have visited: To do this you can just add a new attribute to each node you visit (i.e. node.visited = True). At the beginning (i.e. in the initial loop) of your function play you'd want to make sure that the structure is clean, i.e. set node.visited = False. If you don't want the nodes to be changed after the function play has been called you can just delete them in another loop at the end (i.e. del node.visited).
Possibilities:
Use some sort of node or path disabling, as I suggested last time. Your posting doesn't mention why that doesn't work for you.
Count how many steps you've taken and compare against the node population (call it N). If you take n steps without reaching the end, then you're in a loop.
Try to construct a new list that contains the steps from where you start to the final node. This is a strictly linear linked list, such as START -> 3 -> 6 or START -> 1 -> 2 -> 3 ... If you try to add a node that's already in this linked list, then you have a loop.

Retrieve Graph Lowest Height Node with Filter

Given a Tree T, sometimes binary or not, I need to retrieve the lowest Node that matches a criteria in each branch.
So, I need to retrieve a list (array) of those red marked nodes, where they label is equal to "NP" node.label() == 'NP'.
Actually I'm using NLTK Tree (nltk.tree.Tree) data structure, but you can post the pseudocode only, and I can implement it.
Here is the code that I've tried:
def traverseTree(tree):
if not isinstance(tree, nltk.Tree): return []
h = []
for subtree in tree:
if type(subtree) == nltk.tree.Tree:
t = traverseTree(subtree)
if subtree.label() == 'NP' and len(t) == 0: h.append(subtree)
return h
you have a conditional that if the there are no better candidates for your specification then append subtree, but what if len(t)>0? in that case you want to keep the nodes found in sub calls:
def traverseTree(tree):
if not isinstance(tree, nltk.Tree): return []
h = []
for subtree in tree:
if type(subtree) == nltk.tree.Tree:
t = traverseTree(subtree)
#RIGHT HERE!! need to extend by t or the other found nodes are thrown out
h.extend(t)
if subtree.label() == 'NP' and len(t) == 0:
h.append(subtree)
return h
Keep in mind that if t is always empty you would append all the valid nodes one level below, but any end-of-branch "NP" nodes will be found and returned in t so you want to pass them up a level in the recursion.
Edit: the only case where this would fail is if the top level node is "NP" and there are no sub-nodes of "NP" in which case tree should be added to h:
#after for loop has finished
if len(h) == 0 and tree.label() == "NP":
h.append(tree)
return h
edit2: if you add tree to h then the check for subtrees will never actually come true since they are checking the same node with the same conditionals just in differnt levels of recursion, so you can actually just write the function like this:
def traverseTree(tree):
if not isinstance(tree, nltk.Tree): return []
h = []
for subtree in tree:
#no need to check here as well as right inside the call
h.extend(traverseTree(subtree))
if tree.label() == 'NP' and len(h) == 0:
h.append(tree)
return h

Python A* pathfinding, what is this line doing?

I am reviewing this stack overflow post
Python - Speed up an A Star Pathfinding Algorithm
I am trying to determine what the line for tile in graph[current]: represents. Namely what graph[] represents. I feel like graph should represent the entire grid, but I second guess this because we are giving current as argument to the [] operator on graph, so it has to be returning something, but im not sure what it should be. Maybe the tiles that we can travel to that are directly adjacent to current?
Also what does this syntax mean current = heapq.heappop(openHeap)[1]?
import heapq
def aStar(self, graph, current, end):
openSet = set()
openHeap = []
closedSet = set()
def retracePath(c):
path = [c]
while c.parent is not None:
c = c.parent
path.append(c)
path.reverse()
return path
openSet.add(current)
openHeap.append((0,current))
while openSet:
current = heapq.heappop(openHeap)[1]
if current == end:
return retracePath(current)
openSet.remove(current)
closedSet.add(current)
for tile in graph[current]:
if tile not in closedSet:
tile.H = (abs(end.x-tile.x)+abs(end.y-tile.y))*10
if tile not in openSet:
openSet.add(tile)
heapq.heappush(openHeap, (tile.H,tile))
tile.parent = current
return []
I believe the graph variable is a dict of some sort where the key is the current tile, and the value is a list of all the valid neighboring tiles. That way, every node in the graph is easily accessible via simple dict lookup.
The pseudocode on Wikipedia the author linked to in the original post supports this hypothesis -- the functionally equivalent line is listed as for each neighbor in neighbor_nodes(current)
What the line current = heapq.heappop(openHeap)[1] is doing is returning the literal tile object. If you observe the lines openHeap.append((0,current)) and heapq.heappush(openHeap, (tile.H,tile)), you can observe that the author is adding a tuple of two elements to openHeap where the first element is the heuristic, and the second element is the literal tile object.
Therefore, the line current = heapq.heappop(openHeap)[1] is identical to writing:
temp = heapq.heappop(openHeap)
current = temp[1]
...or to writting:
h, current = heapq.heappop(openHeap)
What the heaqpq.heappop() function itself is doing is returning the smallest element in the heap. Presumably, it's using the first element in the tuple to index, and so will return the open tile with the smallest heuristic as a cheap O(1) operation.

Categories

Resources