I'm trying to build a solution to the N-Puzzle problem using breadth first search in Python.
My solution is adept at finding an answer if all of the numbers bar the zero are in order. e.g.
initial_state = [1,2,3,4,0,5,6,7,8]
or
initial_state = [1,2,3,4,5,6,7,0,8]
but fails with
initial_state = [1,2,5,3,4,0,6,7,8]
Pleases find below my implementation. If someone could point out the flaw in my logic it'd much appreciated.
Thanks in advance!
def right(state):
items = list(state)
i = items.index(0)
n = int(math.sqrt(len(items)))
if (i+1) % n != 0:
del items[i]
items.insert(i+1, 0)
return tuple(items)
else:
pass
def left(state):
items = list(state)
i = items.index(0)
n = int(math.sqrt(len(items)))
if i % n != 0:
del items[i]
items.insert(i-1, 0)
return tuple(items)
else:
pass
def up(state):
items = list(state)
i = items.index(0)
n = int(math.sqrt(len(items)))
if n**2 < i <= (n**2 - n):
del items[i]
items.insert(i+n, 0)
return tuple(items)
else:
pass
def down(state):
items = list(state)
i = items.index(0)
n = int(math.sqrt(len(items)))
if i > n:
del items[i]
items.insert(i-n, 0)
return tuple(items)
else:
pass
class Problem(object):
def __init__(self, initial, goal=None):
self.initial = initial
self.goal = goal
self.n = len(initial)
self.size = int(math.sqrt(self.n))
self.blank = self.initial.index(0)
self.top_row = [i for i in range(self.n) if i < self.size]
self.bottom_row = [i for i in range(self.n) if self.n - (self.size) <= i < self.n]
self.left_column = [i for i in range(self.n) if i % self.size == 0]
self.right_column = [i for i in range(self.n) if (i + 1) % self.size == 0]
def actions(self):
result_list = ["UP","DOWN","LEFT","RIGHT"]
return result_list
def result(self, state, action):
if action == "RIGHT":
return right(state)
if action == "LEFT":
return left(state)
if action == "UP":
return up(state)
if action == "DOWN":
return down(state)
def goal_test(self, state):
return state == self.goal
def path_cost(self, c):
return c + 1
class Node:
def __init__(self, state, parent=None, action=None, path_cost=0):
self.state = state
self.parent = parent
self.action = action
self.path_cost = path_cost
self.depth = 0
if parent:
self.depth = parent.depth + 1
def __repr__(self):
return "<Node %s>" % (self.state,)
def __lt__(self, node):
return self.state < node.state
def expand(self, problem):
return [self.child_node(problem, action)
for action in problem.actions() if self.child_node(problem,action) is not None]
def child_node(self, problem, action):
next = problem.result(self.state, action)
if next:
return Node(next, self, action,
problem.path_cost(self.path_cost))
else:
pass
def solution(self):
return [node.action for node in self.path()[1:]]
def path(self):
node, path_back = self, []
while node:
path_back.append(node)
node = node.parent
return list(reversed(path_back))
def __eq__(self, other):
return isinstance(other, Node) and self.state == other.state
def __hash__(self):
return hash(self.state)
def bfs(problem):
node = Node(problem.initial)
frontier = deque([node])
explored = set()
while frontier:
node = frontier.pop()
explored.add(node.state)
if problem.goal_test(node.state):
return node
for child in node.expand(problem):
if child.state not in explored and child not in frontier:
frontier.append(child)
return [child for child in explored]
p = Problem((1,2,5,3,4,0,6,7,8), (0,1,2,3,4,5,6,7,8))
bfs(p)
#returns
"""[(1, 2, 5, 3, 4, 0, 6, 7, 8),
(1, 2, 0, 5, 3, 4, 6, 7, 8),
(0, 1, 2, 5, 3, 4, 6, 7, 8),
(1, 2, 5, 3, 0, 4, 6, 7, 8),
(1, 2, 5, 0, 3, 4, 6, 7, 8),
(1, 0, 2, 5, 3, 4, 6, 7, 8)]"""
If you process the neighbors (children) of a node (state) by moving the space in UP, DOWN, LEFT, RIGHT order, the solution of an 8-puzzle with bfs starting with the initial state 1,2,5,3,4,0,6,7,8 will be like the following (you can check out where it's differing with your solution):
path_to_goal: ['Up', 'Left', 'Left']
cost_of_path: 3
You may want to refer to this https://sandipanweb.wordpress.com/2017/03/16/using-uninformed-informed-search-algorithms-to-solve-8-puzzle-n-puzzle/?frame-nonce=9e97a821bc for more details.
This condition in up is never true: if n**2 < i <= (n**2 - n).
And this condition in down is off by one: if i > n.
Whether the rest of your code is correct or not is unclear, but you need to debug the fundamentals of your board representation and manipulation code first.
In your space-moving code, I personally would turn your index into an x and y coordinate:
x, y = i % n, i // n
Then you can test more naturally: x>0 for left, x<n-1 for right, y<n-1 for up and y>0 for down.
Related
I am trying to implement Circular Queue in python and trying implement str(self) that print all the elements from the queue from the beginning to the end.When I print out the list, it does not give the whole list of items in the queue.
I am splicing the items from the self.items from the front and going till the end of the list.
class CircularQueue:
def __init__(self, capacity):
self.items = [None] * capacity
self.max_queue = capacity
self.front = 0
self.back = self.max_queue - 1
self.count = 0
def enqueue(self, item):
if not self.is_full():
self.back = (self.back + 1) % self.max_queue
self.items[self.back] = item
self.count += 1
def dequeue(self):
if not self.is_empty():
item = self.items[self.front]
self.front = (self.front + 1) % self.max_queue
self.count -= 1
return item
def is_full(self):
return self.count == self.max_queue
def is_empty(self):
return self.count == 0
def peek(self):
return self.items[self.front]
def __str__(self):
my_list = []
for i in self.items[self.front:]:
my_list.append(i)
for i in self.items[:self.back+1]:
my_list.append(i)
return str(my_list)
q = CircularQueue(4)
print(q)
q.enqueue(1)
q.enqueue(2)
print(q)
q.enqueue(3)
q.enqueue(4)
print(q)
expected result
-> || ->
-> |1, 2| ->
-> |1, 2, 3, 4| ->
got
[None, None, None, None, None, None, None, None]
[1, 2, None, None, 1, 2]
[1, 2, 3, 4, 1, 2, 3, 4]
q = CircularQueue(4)
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.enqueue(4)
q.dequeue()
q.dequeue()
print(q)
q.dequeue()
q.dequeue()
print(q)
expected result
-> |3, 4| ->
-> || ->
got
[3, 4, 1, 2, 3, 4]
[1, 2, 3, 4, 1, 2, 3, 4]
You added two similar for loops, it's just that the first for loop prints everything on the right of the front, and the second for loop prints everything on the left of the back/rear. For example, if we have [1,2,None,None] where the front is at index 0 (value=1) and the rear is at index 1 (value=2) the 2 loops will print out [1,2,None,None,1,2], p.s the code adds 1 to the rear when printing, that's why you get [1,2,None,None,1,2] and not [1,2,None,None,1]
Solution:
replace the 2 for loops with 1 for loop that goes through the elements of the list once, so no splicing is needed
For the second output, I noticed that the dequeue function doesn't do anything, so I made it so that it replaces the item in the front with None
I added # changed on the lines I changed
class CircularQueue:
def __init__(self, capacity):
self.items = [None] * capacity
self.max_queue = capacity
self.front = 0
self.back = self.max_queue - 1
self.count = 0
def enqueue(self, item):
if not self.is_full():
self.back = (self.back + 1) % self.max_queue
self.items[self.back] = item
self.count += 1
def dequeue(self):
if not self.is_empty():
self.items[self.front] = None # changed
self.front = (self.front + 1) % self.max_queue
self.count -= 1
def is_full(self):
return self.count == self.max_queue
def is_empty(self):
return self.count == 0
def peek(self):
return self.items[self.front]
def __str__(self):
my_list = []
for i in self.items: # changed
my_list.append(i)
return str(my_list)
I'm trying to write a function that will find the smallest value in a binary search tree that is greater than some given x (i.e, the successor of x in the BST). The BST class I'm using is the following:
class BSTree:
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def __init__(self):
self.size = 0
self.root = None
def add(self, val):
assert(val not in self)
def add_rec(node):
if not node:
return BSTree.Node(val)
elif val < node.val:
return BSTree.Node(node.val, left=add_rec(node.left), right=node.right)
else:
return BSTree.Node(node.val, left=node.left, right=add_rec(node.right))
self.root = add_rec(self.root)
self.size += 1
def height(self):
"""Returns the height of the longest branch of the tree."""
def height_rec(t):
if not t:
return 0
else:
return max(1+height_rec(t.left), 1+height_rec(t.right))
return height_rec(self.root)
def pprint(self, width=64):
"""Attempts to pretty-print this tree's contents."""
height = self.height()
nodes = [(self.root, 0)]
prev_level = 0
repr_str = ''
while nodes:
n,level = nodes.pop(0)
if prev_level != level:
prev_level = level
repr_str += '\n'
if not n:
if level < height-1:
nodes.extend([(None, level+1), (None, level+1)])
repr_str += '{val:^{width}}'.format(val='-', width=width//2**level)
elif n:
if n.left or level < height-1:
nodes.append((n.left, level+1))
if n.right or level < height-1:
nodes.append((n.right, level+1))
repr_str += '{val:^{width}}'.format(val=n.val, width=width//2**level)
print(repr_str)
I'm trying to write a recursive implementation for my own understanding of recursion, but I'm having some trouble. Here's what I have so far:
def successor(self, x):
def successor_rec(node):
if node is None:
return None
if x < node.val:
if node.left is not None and node.left.val > x:
return successor_rec(node.left)
else:
return node.val
else:
return successor_rec(node.right)
return successor_rec(self.root)
Consider the following BST:
t = BSTree()
for x in [6, 3, 5, 4, 7, 1, 2, 9, 8, 0]:
t.add(x)
t.pprint()
6
3 7
1 5 - 9
0 2 4 - - - 8 -
When I do t.successor(4) I get 6, though I wanted to get 5, the successor of 4 in the tree. I know the problem occurs in the part of the function else: return node.val, though I am struggling to remedy this.
Your if x < node.val block is not right. For instance, even when node.left.val < x, you should still go find the successor of node.left as it could have a right subtree (node.left.right).
Here is a correction:
if x < node.val:
attempt = successor_rec(node.left)
return node.val if attempt is None else attempt
I'm junior programmer, I am trying to solve 8-puzzle problem with breadth first search, but it took too long time to solve it, i want to optimize my code.
Configuration: [[5, 4, 3], [0, 7, 2], [6, 1, 8]] is going to solve in 22.623718615 seconds,
configuration [[8, 0, 6], [5, 4, 7], [2, 3, 1]] took among 235.721346421 seconds.
I want to decrease solve time.
There is my code:
from copy import deepcopy
from collections import deque
from time import perf_counter
most_hard = [[8, 0, 6], [5, 4, 7], [2, 3, 1]] # 30 moves
class CheckPuzzle:
def __init__(self, puzzle: list):
self.puzzle = puzzle
self.len = len(puzzle)
if self.len == 3:
self.goal = [[1, 2, 3],
[4, 5, 6],
[7, 8, 0]]
elif self.len == 4:
self.goal = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 0]]
if not self.is_valid():
raise TypeError("Puzzle is not valid")
elif not self.is_solvable():
raise Exception("Unsolvable puzzle")
# создай ф-ию check
def sum_of_numbers(self) -> int:
return sum(self.convert_to_1d(self.goal))
def sum_of_squares(self) -> int:
return sum([i ** 2 for i in self.convert_to_1d(self.goal)])
def is_valid(self) -> bool:
sum_of_numbers = 0
sum_of_squares = 0
for row in range(self.len):
for column in range(self.len):
sum_of_numbers += self.puzzle[row][column]
sum_of_squares += (self.puzzle[row][column]) ** 2
return sum_of_numbers == self.sum_of_numbers() and sum_of_squares == self.sum_of_squares()
def convert_to_1d(self, board) -> list:
one_dimension_matrix = []
for row in range(self.len):
for column in range(self.len):
one_dimension_matrix.append(board[row][column])
return one_dimension_matrix
def inversion(self, board) -> int:
inversion = 0
one_dimension_matrix = self.convert_to_1d(board)
for index in range(len(one_dimension_matrix)):
temp = one_dimension_matrix[index]
if temp == 0 or temp == 1:
continue
for elem in one_dimension_matrix[index:]:
if elem == 0:
continue
if temp > elem:
inversion += 1
return inversion
def is_solvable(self) -> bool:
inv_of_matrix = self.inversion(self.puzzle)
inv_of_goal_matrix = self.inversion(self.goal)
return (inv_of_matrix % 2 == 0 and inv_of_goal_matrix % 2 == 0) or \
(inv_of_matrix % 2 == 1 and inv_of_goal_matrix % 2 == 1)
class Puzzle:
def __init__(self, board: list):
self.board = board
self.len = len(board)
if self.len == 3:
self.goal = [[1, 2, 3],
[4, 5, 6],
[7, 8, 0]]
elif self.len == 4:
self.goal = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 0]]
def print_matrix(self) -> str:
output = ''
for row in self.board:
for elem in row:
output += str(elem) + " "
output += '\n'
return output
def get_index(self, matrix, value) -> tuple:
for i in range(self.len):
for j in range(self.len):
if matrix[i][j] == value:
return i, j
def manhattan(self):
distance = 0
for i in range(self.len):
for j in range(self.len):
if self.board[i][j] != 0:
x, y = divmod(self.board[i][j] - 1, self.len)
distance += abs(x - i) + abs(y - j)
return distance
def list_of_possible_moves(self) -> list:
x, y = self.get_index(self.board, 0)
possible_moves = []
if x > 0:
possible_moves.append((x - 1, y))
if x < self.len - 1:
possible_moves.append((x + 1, y))
if y > 0:
possible_moves.append((x, y - 1))
if y < self.len - 1:
possible_moves.append((x, y + 1))
return possible_moves
def move(self, to: tuple) -> list:
moving_board = deepcopy(self.board)
x, y = self.get_index(self.board, 0)
i, j = to
moving_board[x][y], moving_board[i][j] = moving_board[i][j], moving_board[x][y]
return moving_board
def solved(self) -> bool:
return self.board == self.goal
def __str__(self) -> str:
return ''.join(map(str, self))
def __iter__(self):
for row in self.board:
yield from row
class Node:
def __init__(self, puzzle, parent=None):
self.puzzle = puzzle
self.parent = parent
if self.parent:
self.g = parent.g + 1
else:
self.g = 0
def state(self) -> str:
return str(self)
def path(self):
node, p = self, []
while node:
p.append(node)
node = node.parent
yield from reversed(p)
def solved(self) -> bool:
return self.puzzle.solved()
def pretty_print(self) -> str:
return self.puzzle.print_matrix()
def h(self) -> int:
return self.puzzle.manhattan()
def f(self) -> int:
return self.h() + self.g
def all_moves(self) -> list:
return self.puzzle.list_of_possible_moves()
def __str__(self) -> str:
return str(self.puzzle)
def make_a_move(self, to: tuple) -> list:
return self.puzzle.move(to)
class GameTree:
def __init__(self, root):
self.root = root
def solve(self):
queue = deque([Node(self.root)])
seen = set()
seen.add(queue[0].state())
while queue:
queue = deque(sorted(list(queue), key=lambda node: node.f()))
node = queue.popleft()
if node.solved():
return node.path()
for move in node.all_moves():
moved = node.make_a_move(move)
child = Node(Puzzle(moved), node)
if child.state() not in seen:
queue.append(child)
seen.add(child.state())
def main():
a = [[5, 4, 3], [0, 7, 2], [6, 1, 8]]
c = Puzzle(a)
d = GameTree(c)
tic = perf_counter()
p = d.solve()
toc = perf_counter()
step = 0
for i in p:
print(i.pretty_print())
step += 1
print(step)
print(toc-tic)
if __name__ == "__main__":
main()
Description:
The 15-puzzle (also called Gem Puzzle, Boss Puzzle, Game of Fifteen,
Mystic Square and many others) is a sliding puzzle that consists of a
frame of numbered square tiles in random order with one tile missing.
The puzzle also exists in other sizes, particularly the smaller
8-puzzle. If the size is 3×3 tiles, the puzzle is called the 8-puzzle
or 9-puzzle, and if 4×4 tiles, the puzzle is called the 15-puzzle or
16-puzzle named, respectively, for the number of tiles and the number
of spaces. The object of the puzzle is to place the tiles in order by
making sliding moves that use the empty space.
https://en.wikipedia.org/wiki/15_puzzle
im teaching my self python and i came across this interesting question which says:
Implement a generator cycle such that if we assign
i = cycle()
then repeated calls to
next(i)
return the values
me
myself
i
me
myself
i
...
I can't use a for loop but only a generator or stream. I cant use libraries. I need to output it 20 times
what I've tried so far but i cant manage to get the cycle to work:
def cycle(i):
saved = []
for el in m:
yield el
saved.append(el)
while saved:
for el in saved:
yield element
This is probably what you want:
def cycle(n=0):
saved=['me','myself','i']
while True:
yield saved[n]
n = (n+1) % 3
i = cycle()
for _ in range(20):
print(next(i))
If you don't want loops at all, try this:
class cycle:
def __init__(self,lst,maxstep = 20):
self.lst = lst
self.n = 0
self.len = len(lst)
self.maxstep = maxstep
def __next__(self):
if self.n>=self.maxstep: # remove this line
raise StopIteration # and this line, if you want infinite stream
ret = self.lst[self.n % self.len]
self.n += 1
return ret
obj = cycle(['me','myself','i'])
If you want to be able to call list on it, that is make it iterable:
class cycle():
def __init__(self,lst,maxstep = 20):
self.lst = lst
self.n = 0
self.i = 0
self.len = len(lst)
self.maxstep = maxstep
def __iter__(self):
while self.i < self.maxstep:
yield self.lst[self.i % self.len]
self.i += 1
def __next__(self):
if self.n>=self.maxstep:
raise StopIteration
ret = self.lst[self.n % self.len]
self.n += 1
return ret
obj = cycle([1, 2, 3])
print(list(obj))
# [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2]
# or
# print(next(obj))
# 1
This question already has an answer here:
Key error '0' with dict format
(1 answer)
Closed 3 years ago.
In the graph class, I had made a vertList dictionary which stores vertex name and vertex class object, the dictionary which throws key error 0 in relax function when calling in Dijkstra function.
I had tried using get function instead of direct calling from the dictionary itself.
from collections import defaultdict
import math
import heapq
class PriorityQueue:
def __init__(self):
self.is_empty = True
self.length = 0
self.pqueue = []
def makePQueue(self, l):
self.pqueue = l.copy()
heapq.heapify(self.pqueue)
if(len(l) > 0):
self.length = len(l)
self.is_empty = False
def addElement(self, element):
heapq.heappush(self.pqueue, element)
self.length = self.length+1
def removeElement(self):
if(self.length > 0):
element = heapq.heappop(self.pqueue)
self.length = self.length-1
if(self.length == 0):
self.is_empty = True
return element
else:
return None
# vertex class
class Vertex:
def __init__(self, key):
self.id = key
self.parent = None
self.distFromSource = math.inf
def getId(self):
return self.id
def getParent(self):
return self.parent
def addParent(self, p):
self.parent = p
def getDistSource(self):
return self.distFromSource
def setDistFromSource(self, d):
self.distFromSource = d
# Graph class
class Graph:
def __init__(self):
self.graph = defaultdict(list)
self.soruce = None
self.vertList = {}
def addVertex(self, v):
if(v not in list(self.vertList.keys())):
ver = Vertex(v)
self.vertList[v] = ver
def addEdge(self, u, v, c):
if(u not in list(self.vertList.keys())):
ver = Vertex(u)
self.vertList[u] = ver
if(v not in list(self.vertList.keys())):
ver = Vertex(v)
self.vertList[v] = ver
self.graph[u].append((v, c))
def getWeight(self, u, v):
# binary search can be implemented for speed
for i in self.graph[u]:
if(i[0] == v):
return i[1]
def setSource(self, s):
if(s not in list(self.vertList.keys())):
ver = Vertex(s)
self.vertList[s] = ver
self.vertList[s].setDistFromSource(0)
self.source = self.vertList[s]
self.source.setDistFromSource(0)
def getSource(self):
return self.source.getId()
def getDistList(self):
l = [(self.vertList[i].getDistSource(), str(i))
for i in list(self.vertList.keys())]
return l
# def costArray(self):
# l = [i.]
# implementation of edge array of cost
def relax(self, u, v, c):
if(self.vertList[v].getDistSource() > self.vertList[u].getDistSource()+c):
self.vertList[v].setDistFromSource(
self.vertList[u].getDistSource()+c)
self.vertList[v].addParent(self.vertList[u])
def dijkstra(graph):
ss = []
l = graph.getDistList()
pq = PriorityQueue()
pq.makePQueue(l)
# print(pq.pqueue)
while(pq.is_empty == False):
(cost, u) = pq.removeElement()
# in priority queue on the basis of cost
if int(u) not in ss:
ss = ss.append(int(u))
for (i, c) in graph.graph[int(u)]:
graph.relax(u, i, c)
```
g = Graph()
g.addEdge(0, 1, 3)
g.addEdge(0, 2, 2)
g.addEdge(0, 3, 5)
g.addEdge(1, 0, 3)
g.addEdge(1, 4, 3)
g.addEdge(4, 1, 3)
g.addEdge(4, 2, 1)
g.addEdge(4, 6, 4)
g.addEdge(2, 0, 2)
g.addEdge(2, 4, 1)
g.addEdge(2, 5, 6)
g.addEdge(3, 0, 5)
g.addEdge(3, 5, 2)
g.addEdge(5, 2, 6)
g.addEdge(5, 3, 2)
g.addEdge(5, 6, 1)
g.addEdge(5, 7, 4)
g.addEdge(6, 4, 4)
g.addEdge(6, 5, 1)
g.addEdge(6, 7, 2)
g.addEdge(7, 5, 4)
g.addEdge(7, 6, 2)
g.setSource(0)
dijkstra(g)
Exception
python graph.py
Traceback (most recent call last):
File "graph.py", line 155, in <module>
dijkstra(g)
File "graph.py", line 126, in dijkstra
graph.relax(u, i, c)
File "graph.py", line 108, in relax
if(self.vertList[v].getDistSource() >
self.vertList[u].getDistSource()+c):
KeyError: '0'
When you call your relax() method, u is a string, not an integer!
That's whhy you get a KeyError: there is indeed a 0 key, but not '0'.
When you defined the priority queue, you explicitly store strings:
def getDistList(self):
l = [(self.vertList[i].getDistSource(), str(i))
for i in list(self.vertList.keys())]
return l
Then, in your dijkstra method, you convert this string to an int in your if statement, but not in the graph.relax() call after that:
if int(u) not in ss:
ss = ss.append(int(u))
for (i, c) in graph.graph[int(u)]:
graph.relax(u, i, c)