Related
This code generates a pascal triangle:
import pprint
def nextRow(cRow):
cRow.append(0)
return [cRow[m - 1] + cRow[m + 1] for m in range(len(cRow) - 1)]
def Pascal(n):
row = [0, 0, 0, 0, 1, 0, 0, 0, 0]
l = []
for h in range(n):
l.append(row)
row = nextRow(row)
return l
pprint.pprint(Pascal(5))
I am trying to remove the extra zeros without just removing them in the end of the code:
Output:
[[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 2, 0, 1, 0, 0, 0],
[0, 1, 0, 3, 0, 3, 0, 1, 0, 0],
[1, 0, 4, 0, 6, 0, 4, 0, 1, 0]]
Desired Output:
[[0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 2, 0, 1, 0, 0],
[0, 1, 0, 3, 0, 3, 0, 1, 0],
[1, 0, 4, 0, 6, 0, 4, 0, 1]]
You can save in l the row calculated without the last element with l.append(row[:-1]) instead of l.append(row) in the Pascal function.
import pprint
def nextRow(cRow):
cRow.append(0)
return [cRow[m - 1] + cRow[m + 1] for m in range(len(cRow) - 1)]
def Pascal(n):
row = [0, 0, 0, 0, 1, 0, 0, 0, 0]
l = []
for h in range(n):
l.append(row[:-1])
row = nextRow(row)
return l
pprint.pprint(Pascal(5))
I am trying to create several arrays from a big array that I have. What I mean is:
data = [[0, 1, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[0, 0, 1, 1, 0, 0, 0, 0, 0,1], [0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 1, 0]]
I want to create 10 different arrays - using the 10 data's columns - with different names.
data1 = [0, 0, 0, 1, 0, 0, 1, 0, 0],
data2 = [1, 0, 1, 0, 0, 0, 0, 1, 0], and so on
I found a close solution here - Also I take the example data from there - However, when I tried the solution suggested:
for d in xrange(0,9):
exec 'x%s = data[:,%s]' %(d,d-1)
A error message appears:
exec(code_obj, self.user_global_ns, self.user_ns)
File "", line 2, in
exec ('x%s = data[:,%s]') %(d,d-1)
File "", line 1
x%s = data[:,%s]
^
SyntaxError: invalid syntax
Please, any comments will be highly appreciated. Regards
Use numpy array index:
data = [[0, 1, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[0, 0, 1, 1, 0, 0, 0, 0, 0,1], [0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 1, 0]]
d = np.array(data)
d[:, 0]
#array([0, 0, 0, 1, 0, 0, 1, 0, 0])
d[:, 1]
#array([1, 0, 1, 0, 0, 0, 0, 1, 0])
etc...
d[:, 9]
#array([0, 0, 1, 1, 1, 0, 0, 0, 0])
If you must, then dictionaries are the way to go:
val = {i:d[:,i] for i in range(d.shape[1])}
To access the arrays:
val[0]
#array([0, 0, 0, 1, 0, 0, 1, 0, 0])
...
val[9]
#array([0, 0, 1, 1, 1, 0, 0, 0, 0])
Use the following code (it is also more readable -- for python 3.x) if you really want to create dynamic variables:
for d in range(0,9):
# exec 'x%s = data[:,%s]' %(d,d-1)
exec(f"data{d} = {data[d]}" )
Either use numpy array as shown by scott boston above or use dictionary like this:
a = {}
for i in range(0,9):
a[i] = data[i][:]
Output:
{0: [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
1: [0, 0, 1, 0, 0, 1, 0, 0, 0, 0],
2: [0, 1, 1, 0, 0, 0, 0, 0, 0, 1],...
I don't see the proper indentation in your for loop.
I suggest you don't use %s for the second argument (string) but rather %d (number) since you need a number to do the indexing of your array.
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]
]
))
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()
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()