optimizing my Battleship field validator 4x4, and fix error - python

I'm a new to stackoverflow, recently I'm trying to solve a problem from codewar with Python 3x and it's about validating battleship location on a 10x10 grid. I passed 33 tests and failed 17 one, which is pretty bad, plus I sometimes get timeout error, so I figured there must be something wrong with my code, I'm open to listen to any opinion, can somebody help me?
def validate_battlefield(field):
# important list comprehension !!!! to add element in a sublist infront and behind
field = [[0] + k + [0] for k in field]
extended_field = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
extended_field.extend(field)
extended_field.extend([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
field = extended_field
# print(extended_field)
list_of_battleship = []
for h in range(1, 11):
for w in range(1, 11):
if field[h][w] == 1:
m, n = h, w
print("*****************")
print(h, n)
length_ship = 1
field[m][n] = -1
if field[h + 1][w] == 0 and field[h][w + 1] == 0:
list_of_battleship.append(1)
break
while field[m][n + 1] + field[m + 1][n] + field[m][n-1] != 2:
if field[m][n + 1] == 1:
print(m, n + 1)
length_ship += 1
field[m][n + 1] = -1
n += 1
if field[m][n + 1] == 0:
list_of_battleship.append(length_ship)
break
if field[m + 1][n] == 1:
print(m + 1, n)
length_ship += 1
field[m + 1][n] = -1
m += 1
if field[m + 1][n] == 0:
list_of_battleship.append(length_ship)
break
list_of_battleship.sort()
if list_of_battleship == [1, 1, 1, 1, 2, 2, 2, 3, 3, 4]:
return True
else:
return False
I can't get the following validation right, I really don't know where went wrong
print(validate_battlefield([
[1, 0, 0, 0, 0, 1, 1, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0],
[1, 0, 1, 0, 1, 1, 1, 0, 1, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],]
))
print(validate_battlefield(
[
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 1, 1, 1, 0, 0, 0, 0]
]
))

Related

Why am i getting an error with this line of code, when the logic is used else where and it has no issues?

Im trying to write Conways Game of Life, and for some reason 1 line of code comes up with an error even though it's same logic has been used elsewhere in the code and is fine.
Code:
origin = [
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
c = origin
current_cycle = 1
while current_cycle < (x_cycle + 1):
for i in origin:
for j in i:
count = 0
x = i #X-Coordinate
y = j #Y-Coordinate
elif y == 0:
if x == 0: #Top Left Corner
if c[x + 1][j] == 1:
count += 1
if c[x][j + 1] == 1:
count += 1
if c[x + 1][j + 1]:
count += 1
elif x == c[-1][-1]: #Top Right Corner
if c[x - 1][y] == 1:
count += 1
if c[x-1][y + 1] == 1:
count += 1
if c[x][y + 1] == 1:
count += 1
else: #Top Left to Top Right
if c[x-1][y] == 1:
count += 1
if c[x-1][y+1] == 1:
count += 1
if c[x][y+1] == 1:
count += 1
if c[x+1][y+1] == 1:
count += 1
if c[x+1][y] == y:
count += 1
The error I am getting is:
line 111, in gameoflife
if c[x-1][y] == 1:
TypeError: unsupported operand type(s) for -: 'list' and 'int'
Any help/advice is appreciated
Thanks!

Maze pathfinding implementation (BFS) not giving correct path [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I am trying to get the shortest path for a maze with a ball: the ball is rolling until it hits a wall. I use Dijkstra's algorithm using heapq for priority queue. However, I get a non-optimal path as result.
Here is my code with sample input:
maze = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0],
[0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1],
[1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0],
[0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0],
[0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
start = (0, 0)
end = (22, 22)
def shortestDistance(maze: List[List[int]], start: List[int], destination: List[int]):
start, destination = tuple(start), tuple(destination)
row, col = len(maze), len(maze[0])
moves = [(-1, 0), (0, 1), (0, -1), (1, 0)]
dstr = ['u', 'r', 'l', 'd']
class Point:
def __init__(self, distance, coordinates, directions):
self.distance = distance
self.coordinates = coordinates
self.directions = directions
def __eq__(self, p):
if self.distance == p.distance:
return self.__lt__(self, p)
return self.distance - p.distance
def __lt__(self, p):
return len(self.directions) - len(p.directions)
heap = [(Point(0, start, ""))]
visited = set()
while heap:
point = heapq.heappop(heap)
dist = point.distance
node = point.coordinates
directions = point.directions
if node in visited: continue
if node == destination:
return directions
visited.add(node)
for idx, move in enumerate(moves):
dx, dy = move
newX = node[0]
newY = node[1]
distance = dist
newDirections = directions
while 0 <= newX + dx < row and 0 <= newY + dy < col and maze[newX + dx][newY + dy] == 0:
newX += dx
newY += dy
distance += 1
if (newX, newY) == destination:
break
if (newX, newY) not in visited:
heapq.heappush(heap, Point(distance, (newX, newY), newDirections + dstr[idx]))
return "Impossible"
path = shortestDistance(maze, start, end)
print(path)
The idea is to compare the distance and if it is equal, pick the path with fewer changes of direction.
I am currently getting rdrludlrdrudludldldr (i.e. right-down-right-left-...) as an output, but the sequence "rl" found at index 2 doesn't make sense: "Right" should not be followed by "Left" nor should "Up" be followed by "Down" and vice versa. Such a sequence is evidently not optimal as the first of those two moves could just be omitted to get the ball at the same location and travelling shorter distance.
The expected output for this maze is drururdrdrurdrd.
Why am I not getting the shortest path?
The problem is that the __lt__ function is not doing what it should.
It should return a boolean which is true when self is to be considered less than p. As you currently return an integer result, which often is non-zero you get into the situation where a pair (p, q) of points would have both p < q and q < p as true... which leads to erratic behaviour.
Here is how you could define it:
def __lt__(self, p):
return ((self.distance, len(self.directions), self.directions) <
< (p.distance, len(p.directions), p.directions))
With this change the returned path is
rdrdldldrdr
Simplification
Instead of creating the class Point, you could use named tuples, which makes everything easier (and faster). You would just need to change the order of the "properties" so that these points compare in the desired way, i.e. directions should come before coordinates and the length of the directions string should get its own property:
from collections import namedtuple
# change order of properties so comparison works as intended
Point = namedtuple("Point", "distance, length, directions, coordinates")
And then make the appropriate change where you call Point:
heap = [Point(0, 0, "", start)]
# ...
heapq.heappush(heap, Point(distance, len(newDirections) + 1, newDirections + dstr[idx], (newX, newY)))

Two-Dimensional Arrays not updating correctly in python 3

So I am trying to create a simple world generation using code that I currently understand. I am doing this by creating a 2-dimensional array using 0's as nothing and 1's as a drawing function. I first create a blank world using input variables and then I plan to update the array using a generation script. However when trying to update world[0][x] it updates every item at that "x" location throughout every single list
Here is my code:
import random
worldHeight = 10 #int(input("What is the world height? "))
worldLength = 5 #int(input("What is the world length? "))
terrainHeight = 5 #int(input("What is the terrain height? "))
step = 2 #int(input("What is the step? "))
world = []
worldBlankRow = []
def createBlank():
global worldLength, worldHeight, world, worldBlankRow
for n in range(0,worldHeight):
worldBlankRow.append(0)
for n in range(0,worldLength):
world.append(worldBlankRow)
print(world)
def generate():
global world, worldHeight, worldLength,terrainHeight
counter=0
#randomStep = random.randint(-(step),step)
#while counter <= worldLength:
for x in range(worldHeight-terrainHeight,worldHeight):
world[0][x] = 1
print(world)
#counter=counter+1
#print(counter)
createBlank()
generate()
and here is my output so you can see what is going wrong:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
[[0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]]
[[0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0]]
[[0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0]]
[[0, 0, 0, 0, 0, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0]]
[[0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]]
as you can see every list is updating where i want to generate one, then the next and then the next.
The problem is that you append worldBlankRow to world. Now this means that the exact same object is appended many times to world. Later, when you change one element of world, you change them all, because they are all really pointing to the same worldBlankRow. You need a separate copy for each. Try:
import random
import copy
worldHeight = 10 #int(input("What is the world height? "))
worldLength = 5 #int(input("What is the world length? "))
terrainHeight = 5 #int(input("What is the terrain height? "))
step = 2 #int(input("What is the step? "))
world = []
worldBlankRow = []
def createBlank():
global worldLength, worldHeight, world, worldBlankRow
for n in range(0,worldHeight):
worldBlankRow.append(0)
for n in range(0,worldLength):
world.append(copy.copy(worldBlankRow))
print(world)
def generate():
global world, worldHeight, worldLength,terrainHeight
counter=0
#randomStep = random.randint(-(step),step)
#while counter <= worldLength:
for x in range(worldHeight-terrainHeight,worldHeight):
world[0][x] = 1
print(world)
#counter=counter+1
#print(counter)
createBlank()
generate()

Avoid a deepcopy when doing a BFS

I'm currently solving the second exercise in this assignment (this is not homework, I'm actually trying to solve this other problem). My solution uses a BFS to search for the minimal solution to a variant of the "Lights Out" problem, in which pressing a light will flip the state of every light on the same row and the same column.
I think that my implementation is correct, but it's a bit too slow: it's currently taking 12+ seconds to run on my computer (which is unacceptable for my purposes).
from copy import deepcopy
from itertools import chain
from Queue import PriorityQueue
# See: http://www.seas.upenn.edu/~cis391/Homework/Homework2.pdf
class Puzzle(object):
def __init__(self, matrix):
self.matrix = matrix
self.dim = len(matrix)
def __repr__(self):
return str(self.matrix)
def solved(self):
return sum([sum(row) for row in self.matrix]) == 0
def move(self, i, j):
for k in range(self.dim):
self.matrix[i][k] = (self.matrix[i][k] + 1) % 2
self.matrix[k][j] = (self.matrix[k][j] + 1) % 2
self.matrix[i][j] = (self.matrix[i][j] + 1) % 2
return self
def copy(self):
return deepcopy(self)
def next(self):
result = []
for i in range(self.dim):
for j in range(self.dim):
result.append(self.copy().move(i, j))
return result
def solve(self):
q = PriorityQueue()
v = set()
q.put((0, self))
while True:
c = q.get()
if c[1].solved():
return c[0]
else:
for el in c[1].next():
t = el.tuple()
if t not in v:
v.add(t)
q.put((c[0] + 1, el))
def tuple(self):
return tuple(chain.from_iterable(self.matrix))
The culprit, according to cProfile, appears to be the deepcopy call. On the other hand, I see no alternatives: I need to add to the queue another Puzzle object containing a fresh copy of self.matrix.
How can I speed up my implementation?
Here's the test case that I'm using:
print Puzzle([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]).solve()
which should return 1 (we only need to press the light in the lower right corner).
EDIT: Here's another gnarly test case:
print Puzzle([
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
]).solve()
Its solution is at most 14: press all lights on the diagonal that were already on. Unfortunately, the impressive speedup by #zch isn't enough to solve this problem, leading me to believe that, due to the high branching factor, a BFS wasn't the right way to solve this problem.
There is a number of optimizations to be done.
First, avoid deepcopy, implement it your own copying (this by itself worked for me 5x faster):
class Puzzle(object):
def __init__(self, matrix):
self.matrix = [list(row) for row in matrix]
self.dim = len(matrix)
def copy(self):
return Puzzle(self.matrix)
Second, in BFS you don't need priority queue, use Queue or implement your own queuing. This gives some speedup. And third, check for being solved before putting it into the queue, not after taking things out. This should allow you to go one level deeper in comparable time:
def solve(self):
v = set()
q = [(0, self)]
i = 0
while True:
c = q[i]
i += 1
for el in c[1].next():
t = el.tuple()
if t not in v:
if el.solved():
return c[0] + 1
v.add(t)
q.append((c[0] + 1, el))
Further, using a list of list of bits is very memory-inefficient. You can pack all the bits into a single integer and get much faster solution. Additionally you can precompute masks for allowed moves:
def bits(iterable):
bit = 1
res = 0
for elem in iterable:
if elem:
res |= bit
bit <<= 1
return res
def mask(dim, i, j):
res = 0
for idx in range(dim * i, dim * (i + 1)):
res |= 1 << idx
for idx in range(j, dim * dim, dim):
res |= 1 << idx
return res
def masks(dim):
return [mask(dim, i, j) for i in range(dim) for j in range(dim)]
class Puzzle(object):
def __init__(self, matrix):
if isinstance(matrix, Puzzle):
self.matrix = matrix.matrix
self.dim = matrix.dim
self.masks = matrix.masks
else:
self.matrix = bits(sum(matrix, []))
self.dim = len(matrix)
self.masks = masks(len(matrix))
def __repr__(self):
return str(self.matrix)
def solved(self):
return self.matrix == 0
def next(self):
for mask in self.masks:
puzzle = Puzzle(self)
puzzle.matrix ^= mask
yield puzzle
def solve(self):
v = set()
q = [(0, self)]
i = 0
while True:
c = q[i]
i += 1
for el in c[1].next():
t = el.matrix
if t not in v:
if el.solved():
return c[0] + 1
v.add(t)
q.append((c[0] + 1, el))
And finally for another factor of 5 you can pass around just bit matrices, instead of whole Puzzle objects and additionally inline some most often used function.
def solve(self):
v = set()
q = [(0, self.matrix)]
i = 0
while True:
dist, matrix = q[i]
i += 1
for mask in self.masks:
t = matrix ^ mask
if t not in v:
if t == 0:
return dist + 1
v.add(t)
q.append((dist + 1, t))
For me these optimizations combined give speedup of about 250 times.
I changed solve to
def solve(self):
q = PriorityQueue()
v = set()
q.put((0, self))
while True:
c = q.get()
if c[1].solved():
return c[0]
else:
for i in range(self.dim):
for j in range(self.dim):
el = c[1].move(i, j) # do the move
t = el.tuple()
if t not in v:
v.add(t)
q.put((c[0] + 1, el.copy())) # copy only as needed
c[1].move(i, j) # undo the move
As .move(i, j) is its own inverse. Copies are made but only when the state has not been visited. This reduces the time from 7.405s to 5.671s. But this is not as big an improvement as expected.
Then replacing def tuple(self): with:
def tuple(self):
return tuple(tuple(r) for r in self.matrix)
reduces the time from 5.671s to 0.531s. That should do it.
Full listing:
from copy import deepcopy
from Queue import PriorityQueue
# See: http://www.seas.upenn.edu/~cis391/Homework/Homework2.pdf
class Puzzle(object):
def __init__(self, matrix):
self.matrix = matrix
self.dim = len(matrix)
def __repr__(self):
return str(self.matrix)
def solved(self):
return sum([sum(row) for row in self.matrix]) == 0
def move(self, i, j):
for k in range(self.dim):
self.matrix[i][k] = (self.matrix[i][k] + 1) % 2
self.matrix[k][j] = (self.matrix[k][j] + 1) % 2
self.matrix[i][j] = (self.matrix[i][j] + 1) % 2
return self
def copy(self):
return deepcopy(self)
def solve(self):
q = PriorityQueue()
v = set()
q.put((0, self))
while True:
c = q.get()
if c[1].solved():
return c[0]
else:
for i in range(self.dim):
for j in range(self.dim):
el = c[1].move(i, j) # do the move
t = el.tuple()
if t not in v:
v.add(t)
q.put((c[0] + 1, el.copy())) # copy only as needed
c[1].move(i, j) # undo the move
def tuple(self):
return tuple(tuple(r) for r in self.matrix)
print Puzzle([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]).solve()

Longest Common Sequence -Index Error

I am trying to find the LCS between two sequences: TACGCTGGTACTGGCAT and AGCTGGTCAGAA. I want my answer to output as a matrix so that I can backtrack which sequence is common (GCTGGT). When I use my code below, I am getting the following error. IndexError: list index out of range. How can I avoid this error in my code below?
def LCS(x, y):
m = len(x)
n = len(y)
C = []
for i in range(m):
for j in range(n):
if x[i] == y[j]:
C[i][j] == C[i-1][j-1] + 1
else:
C[i][j] == 0
return C
x = "TACGCTGGTACTGGCAT"
y = "AGCTGGTCAGAA"
m = len(x)
n = len(y)
C = LCS(x, y)
print C
You need to append to your list, because the index [i][j] does not exist yet.
def LCS(x, y):
m = len(x)
n = len(y)
C = []
for i in range(m):
C.append([]) # append a blank list at index [i]
for j in range(n):
if x[i] == y[j]:
C[i].append(C[i-1][j-1] + 1) # append current element to [i]
else:
C[i].append(0)
return C
Testing
x = "TACGCTGGTACTGGCAT"
y = "AGCTGGTCAGAA"
LCS(x,y)
Output
[[0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0],
[0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 3, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 4, 1, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 5, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 6, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 3, 1, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 4, 0, 0, 0, 1, 0, 0],
[0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1],
[0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0]]

Categories