Python creating 2D array - python

num1 = [1,2,3,4,5]
num2 = [1,2,3,4,5]
arr1 = [[0]*(len(num2)+1)]*(len(num1)+1)
arr2 = [[0 for _ in range(len(num2)+1)] for _ in range(len(num1)+1)]
I get a different answer when I define arr1 and arr2.
Aren't arr1 and arr2 create the same 2D array?

They are not the same. arr1 is a list with (len(nums1)+1) references to the same list [0]*(len(nums2)+1). So when you modify an element in one of them, all references will see this change as well.
For example,
>>> arr1[0][0] += 1
>>> print(arr1)
[[1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0]]
arr2 doesn't suffer from this problem because it has len(nums1)+1 distinct lists:
>>> arr2[0][0] += 1
>>> print(arr2)
[[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]]

A better way to see the difference is to use a random number to fill the entries.
from random import randrange
num1 = [1,2,3,4,5]
num2 = [1,2,3,4,5]
arr1 = [[randrange(10)]*(len(nums2)+1)]*(len(nums1)+1)
arr2 = [[randrange(10) for _ in range(len(nums2)+1)] for _ in range(len(nums1)+1)]
print(arr1)
print(arr2)
The output is:
[[5, 5, 5, 5, 5, 5], [5, 5, 5, 5, 5, 5], [5, 5, 5, 5, 5, 5], [5, 5, 5, 5, 5, 5], [5, 5, 5, 5, 5, 5], [5, 5, 5, 5, 5, 5]]
[[7, 4, 2, 4, 0, 3], [7, 5, 1, 0, 1, 7], [4, 4, 1, 0, 2, 1], [2, 3, 6, 2, 6, 7], [6, 6, 6, 0, 3, 3], [0, 4, 5, 0, 6, 6]]
You can see that for the arr1, it populates every entry with the same number; while for arr2, the entries are all truly random. This is because arr1 is constructed by expanding a list of just one number, which is [5] here.

Related

Finding spots in a numpy matrix

I have the following Matrix made of 0s and 1s which I want to identify its spots(elements with the value 1 and connected to eachothers).
M = np.array([[1,1,1,0,0,0,0,0,0,0,0],
[1,1,1,0,0,0,0,0,0,1,1],
[1,1,1,0,0,0,0,0,0,1,1],
[1,1,1,0,0,1,1,1,0,0,0],
[0,0,0,0,0,1,1,1,0,0,0],
[1,1,1,0,1,1,1,1,0,0,0],
[1,1,1,0,0,1,1,1,0,0,0],
[1,1,1,0,0,1,1,1,0,0,0]])
In the matrix there are four spots.
an example of my output should seem the following
spot_0 = array[(0,0),(0,1), (0,2), (1,0),(1,1), (1,2), (2,0),(2,1), (2,2), (3,0),(3,1), (3,2)]
Nbr_0 = 12
Top_Left = (0, 0)
and that is the same process for the other 3 spots
Does anyone know how can I identify each spot with the number of its elements and top_left element, using numpy functions ?
Thanks
You can use a connected component labeling to find the spots. Then, you can use np.max so to find the number of component and np.argwhere so to find the locations of each component. Here is an example:
# OpenCV provides a similar function
from skimage.measure import label
components = label(M)
# array([[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
# [1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2],
# [1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2],
# [1, 1, 1, 0, 0, 3, 3, 3, 0, 0, 0],
# [0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0],
# [4, 4, 4, 0, 3, 3, 3, 3, 0, 0, 0],
# [4, 4, 4, 0, 0, 3, 3, 3, 0, 0, 0],
# [4, 4, 4, 0, 0, 3, 3, 3, 0, 0, 0]])
for i in range(1, np.max(components)+1):
spot_i = np.argwhere(components == i)
Nbr_i = len(spot_i)
Top_Left_i = spot_i[0]
Note that Top_Left only make sense for a rectangular area. If they are not rectangular this point needs to be carefully defined.
Note also that this method is only efficient with few component. If there are many component, then it is better to replace the current loop by an iteration over the components array (in this case the output structure is stored in a list l and l[components[i,j]] is updated with the information found for all item location (i,j) of components). This last algorithm will be slow unless Numba/Cython are used to speed the process up.
You could use skimage.measure.label or other tools (for instance, OpenCV or igraph) to create labels for connected components:
#from #Jérôme's answer
from skimage.measure import label
components = label(M)
# array([[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
# [1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2],
# [1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2],
# [1, 1, 1, 0, 0, 3, 3, 3, 0, 0, 0],
# [0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0],
# [4, 4, 4, 0, 3, 3, 3, 3, 0, 0, 0],
# [4, 4, 4, 0, 0, 3, 3, 3, 0, 0, 0],
# [4, 4, 4, 0, 0, 3, 3, 3, 0, 0, 0]])
In the later part you could create a one-dimensional view of image, sort values of pixels and find dividing points of sorted label values:
components_ravel = components.ravel()
c = np.arange(1, np.max(components_ravel) + 1)
argidx = np.argsort(components_ravel)
div_points = np.searchsorted(components_ravel, c, sorter=argidx)
# Sorted label values are:
# [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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
# 2, 2, 2, 2
# 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
# 4, 4, 4, 4, 4, 4, 4, 4, 4
# So you find indices that divides these groups:
# [47, 59, 63, 79]
After that you could split array of indices that sorts your one-dimensional view of image at these points and convert them into two-dimensional ones:
spots = []
for n in np.split(argidx, div_points)[1:]: #in case there are no zeros, cancel `[1:]`
x, y = np.unravel_index(n, components.shape)
spots.append(np.transpose([x, y]))
It creates a list of spot coordinates of each group:
[array([[1, 0], [1, 2], [0, 2], [0, 1], [1, 1], [0, 0], [2, 2], [2, 1], [2, 0], [3, 2], [3, 1], [3, 0]]),
array([[2, 10], [1, 9], [2, 9], [1, 10]]),
array([[6, 5], [7, 5], [7, 6], [7, 7], [6, 7], [6, 6], [3, 5], [4, 6], [3, 6], [4, 5], [3, 7], [5, 7], [5, 6], [4, 7], [5, 5], [5, 4]]),
array([[5, 0], [5, 1], [5, 2], [6, 2], [7, 0], [6, 0], [6, 1], [7, 1], [7, 2]])]
Note that an order of pixels of each group is mixed. This is because np.argsort uses a sort which is not stable. You could fix it like so:
argidx = np.argsort(components_ravel, kind='stable')
In this case you'll get:
[array([[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2], [3, 0], [3, 1], [3, 2]]),
array([[1, 9], [1, 10], [2, 9], [2, 10]]),
array([[3, 5], [3, 6], [3, 7], [4, 5], [4, 6], [4, 7], [5, 4], [5, 5], [5, 6], [5, 7], [6, 5], [6, 6], [6, 7], [7, 5], [7, 6], [7, 7]]),
array([[5, 0], [5, 1], [5, 2], [6, 0], [6, 1], [6, 2], [7, 0], [7, 1], [7, 2]])]

Change one element each time and get all possible states

I have one entry [0, 0, 0, 0].
Each element of the entry has three possible values 0, 1, 2.
We can only change one value each time. For example, one of the possible state is [1, 0, 0, 0].
I want to get all possible states.
I solved this by the following code. Can anyone provide a more concise code? Thanks a lot!
entry = [0, 0, 0, 0]
state = set([0,1,2])
result = []
import copy
for i in range(len(entry)):
for ele in state - set([entry[i]]):
entry_c = copy.deepcopy(entry)
entry_c[i] = ele
result.append(entry_c)
The result:
[1, 0, 0, 0]
[2, 0, 0, 0]
[0, 1, 0, 0]
[0, 2, 0, 0]
[0, 0, 1, 0]
[0, 0, 2, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
You can use itertools.permutations:
import itertools
list(set([i for i in itertools.permutations([0,0,0,1])]))+list(set([i for i in itertools.permutations([0,0,0,2])])
Output:
[(0, 0, 0, 1), (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 2), (0, 2, 0, 0), (0, 0, 2, 0), (2, 0, 0, 0)]
A more generic solution for any entry and any state is this:
[entry[:i]+[state[k]]+entry[i+1:] for i in range(len(entry)) for k in state]
An example with its output:
entry=[2,3,5,7]
state=[0,1,2]
output--> [[0, 3, 5, 7], [2, 0, 5, 7], [2, 3, 0, 7], [2, 3, 5, 0], [1, 3, 5, 7], [2, 1, 5, 7], [2, 3, 1, 7], [2, 3, 5, 1], [2, 3, 5, 7], [2, 2, 5, 7], [2, 3, 2, 7], [2, 3, 5, 2]]
Inspired by #archer, I get the following answer.
entry = [0,0,0,0]
state = [0,1,2]
ans = set()
# replace the value of each position with one of all possible states
for i in range(len(entry)):
for ele in state:
tmp = entry[:i] + [ele] + entry[i+1:]
ans.add(tuple(tmp))
print(ans)
print(ans - set([tuple(entry)]))

Generate Numpy array of even integers that sum to a value

Is there a numpy solution that would allow you to initialize an array based on the following conditions?
Number of elements in axis 1. (In the example below you have 4 places in each element of the array)
Sum of values. (All elements sum to 8)
Step size. (Using increments of 2)
Essentially this shows all the combinations of 4 values you can add to achieve the wanted sum (8) at a step size of 2.
My experiments fail when I set the axis 1 dimension to over 6 and the sum to over 100.
There has to be a better way to do this than what I've been trying.
array([[0, 0, 0, 8],
[0, 0, 2, 6],
[0, 0, 4, 4],
[0, 0, 6, 2],
[0, 0, 8, 0],
[0, 2, 0, 6],
[0, 2, 2, 4],
[0, 2, 4, 2],
[0, 2, 6, 0],
[0, 4, 0, 4],
[0, 4, 2, 2],
[0, 4, 4, 0],
[0, 6, 0, 2],
[0, 6, 2, 0],
[0, 8, 0, 0],
[2, 0, 0, 6],
[2, 0, 2, 4],
[2, 0, 4, 2],
[2, 0, 6, 0],
[2, 2, 0, 4],
[2, 2, 2, 2],
[2, 2, 4, 0],
[2, 4, 0, 2],
[2, 4, 2, 0],
[2, 6, 0, 0],
[4, 0, 0, 4],
[4, 0, 2, 2],
[4, 0, 4, 0],
[4, 2, 0, 2],
[4, 2, 2, 0],
[4, 4, 0, 0],
[6, 0, 0, 2],
[6, 0, 2, 0],
[6, 2, 0, 0],
[8, 0, 0, 0]], dtype=int64)
Here is a small code that will enable you to loop over the desired combinations. It takes 3 parameter:
itsize: Number of elements.
itsum: Sum of values.
itstep: Step size.
It may be necessary to optimize it if the computations you do in the FOR loop are light. I loop over more combinations than necessary (all the i,j,k,l that take values in 0,itstep,2*itstep,...,itsum) and keep only those verifying the condition that all sum up to itsum. The big size array is not computed and the rows are computed on-the-fly when iterating so you will not have the memory troubles:
class Combinations:
def __init__(self, itsize, itsum, itstep):
assert(itsum % itstep==0) # Sum is a multiple of step
assert(itsum >= itstep) # Sum bigger or equal than step
assert(itsize > 0) # Number of elements >0
self.itsize = itsize # Number of elements
self.itsum = itsum # Sum parameter
self.itstep = itstep # Step parameter
self.cvalue = None # Value of the iterator
def __iter__(self):
self.itvalue = None
return self
def __next__(self):
if self.itvalue is None: # Initialization of the iterator
self.itvalue = [0]*(self.itsize)
elif self.itvalue[0] == self.itsum: # We reached all combinations the iterator is restarted
self.itvalue = None
return None
while True: # Find the next iterator value
for i in range(self.itsize-1,-1,-1):
if self.itvalue[i]<self.itsum:
self.itvalue[i] += self.itstep
break
else:
self.itvalue[i] = 0
if sum(self.itvalue) == self.itsum:
break
return self.itvalue # Return iterator value
myiter = iter(Combinations(4,8,2))
for val in myiter:
if val is None:
break
print(val)
Output:
% python3 script.py
[0, 0, 0, 8]
[0, 0, 2, 6]
[0, 0, 4, 4]
[0, 0, 6, 2]
[0, 0, 8, 0]
[0, 2, 0, 6]
[0, 2, 2, 4]
[0, 2, 4, 2]
[0, 2, 6, 0]
[0, 4, 0, 4]
[0, 4, 2, 2]
[0, 4, 4, 0]
[0, 6, 0, 2]
[0, 6, 2, 0]
[0, 8, 0, 0]
[2, 0, 0, 6]
[2, 0, 2, 4]
[2, 0, 4, 2]
[2, 0, 6, 0]
[2, 2, 0, 4]
[2, 2, 2, 2]
[2, 2, 4, 0]
[2, 4, 0, 2]
[2, 4, 2, 0]
[2, 6, 0, 0]
[4, 0, 0, 4]
[4, 0, 2, 2]
[4, 0, 4, 0]
[4, 2, 0, 2]
[4, 2, 2, 0]
[4, 4, 0, 0]
[6, 0, 0, 2]
[6, 0, 2, 0]
[6, 2, 0, 0]
[8, 0, 0, 0]
I tried this out and also found that it slowed down significantly at that size. I think part of the problem is that the output array gets pretty large at that point. I'm not 100% sure my code is right, but the plot shows how the array size grows with condition 2 (sum of values in each row). I didn't do 100, but it looks like it would be about 4,000,000 rows
plot

How to improve a backtraching sudoku solving algorithm in Python? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I wrote a backtracking Sudoku solving algorithm in Python.
It solves a 2D array like this (zero means "empty field"):
[
[7, 0, 0, 0, 0, 9, 0, 0, 3],
[0, 9, 0, 1, 0, 0, 8, 0, 0],
[0, 1, 0, 0, 0, 7, 0, 0, 0],
[0, 3, 0, 4, 0, 0, 0, 8, 0],
[6, 0, 0, 0, 8, 0, 0, 0, 1],
[0, 7, 0, 0, 0, 2, 0, 3, 0],
[0, 0, 0, 5, 0, 0, 0, 1, 0],
[0, 0, 4, 0, 0, 3, 0, 9, 0],
[5, 0, 0, 7, 0, 0, 0, 0, 2],
]
like this:
[
[7, 5, 8, 2, 4, 9, 1, 6, 3],
[4, 9, 3, 1, 5, 6, 8, 2, 7],
[2, 1, 6, 8, 3, 7, 4, 5, 9],
[9, 3, 5, 4, 7, 1, 2, 8, 6],
[6, 4, 2, 3, 8, 5, 9, 7, 1],
[8, 7, 1, 9, 6, 2, 5, 3, 4],
[3, 2, 7, 5, 9, 4, 6, 1, 8],
[1, 8, 4, 6, 2, 3, 7, 9, 5],
[5, 6, 9, 7, 1, 8, 3, 4, 2]
]
But for "hard" Sudokus (where there are a lot of zeros at the beginning), it's quite slow. It takes the algorithm around 9 seconds to solve the Sudoku above. That's a lot better then what I startet with (90 seconds), but still slow.
I think that the "deepcopy" can somehow be improved/replaced (because it is executed 103.073 times in the example below), but my basic approaches were slower..
I heard of 0.01 second C/C++ solutions but I'm not sure if those are backtracking algorithms of some kind of mathematical solution...
This is my whole algorithm with 2 example Sudokus:
from copy import deepcopy
def is_sol_row(mat,row,val):
m = len(mat)
for i in range(m):
if mat[row][i] == val:
return False
return True
def is_sol_col(mat,col,val):
m = len(mat)
for i in range(m):
if mat[i][col] == val:
return False
return True
def is_sol_block(mat,row,col,val):
rainbow = [0,0,0,3,3,3,6,6,6]
i = rainbow[row]
j = rainbow[col]
elements = {
mat[i + 0][j + 0], mat[i + 1][j + 0], mat[i + 2][j + 0],
mat[i + 0][j + 1], mat[i + 1][j + 1], mat[i + 2][j + 1],
mat[i + 0][j + 2], mat[i + 1][j + 2], mat[i + 2][j + 2],
}
if val in elements:
return False
return True
def is_sol(mat,row,col,val):
return is_sol_row(mat,row,val) and is_sol_col(mat,col,val) and is_sol_block(mat,row,col,val)
def findAllZeroIndizes(mat):
m = len(mat)
indizes = []
for i in range(m):
for j in range(m):
if mat[i][j] == 0:
indizes.append((i,j))
return indizes
def sudoku(mat):
q = [(mat,0)]
zeroIndizes = findAllZeroIndizes(mat)
while q:
t,numSolvedIndizes = q.pop()
if numSolvedIndizes == len(zeroIndizes):
return t
else:
i,j = zeroIndizes[numSolvedIndizes]
for k in range(1,10):
if is_sol(t,i,j,k):
newt = deepcopy(t)
newt[i][j] = k
q.append((newt,numSolvedIndizes+1))
return False
mat = [
[7, 0, 0, 0, 0, 9, 0, 0, 3],
[0, 9, 0, 1, 0, 0, 8, 0, 0],
[0, 1, 0, 0, 0, 7, 0, 0, 0],
[0, 3, 0, 4, 0, 0, 0, 8, 0],
[6, 0, 0, 0, 8, 0, 0, 0, 1],
[0, 7, 0, 0, 0, 2, 0, 3, 0],
[0, 0, 0, 5, 0, 0, 0, 1, 0],
[0, 0, 4, 0, 0, 3, 0, 9, 0],
[5, 0, 0, 7, 0, 0, 0, 0, 2],
]
# mat = [
# [3, 0, 6, 5, 0, 8, 4, 0, 0],
# [5, 2, 0, 0, 0, 0, 0, 0, 0],
# [0, 8, 7, 0, 0, 0, 0, 3, 1],
# [0, 0, 3, 0, 1, 0, 0, 8, 0],
# [9, 0, 0, 8, 6, 3, 0, 0, 5],
# [0, 5, 0, 0, 9, 0, 6, 0, 0],
# [1, 3, 0, 0, 0, 0, 2, 5, 0],
# [0, 0, 0, 0, 0, 0, 0, 7, 4],
# [0, 0, 5, 2, 0, 6, 3, 0, 0]
# ]
print(sudoku(mat))
The largest time sink is that, for every open position, you try each of the nine digits, without learning anything about the attempts. Your test grid has 56 open grid locations, so anything you do is magnified through that lens. A little preprocessing will go a long way. For instance, make a list of available numbers in each row and column. Key that appropriately, and use that for your search instead of range(m).
Another technique is to apply simple algorithms to make trivial placements as they become available. For instance, you can quickly derive the 1 in the upper-left block, and the missing 7s in the left and middle columns of blocks. This alone cuts the solution time in half. Wherever you're down to a single choice for what number goes in a selected open square, or where a selected number can be placed in a particular row/col/block, then make that placement before you engage in exhaustive backtracking.

How to set array entries to zero in Python

I have an array
A = [[1, 2, 4, 0, -2, 6],
[3, 5, 4, 9, 10, -3],
[4, 6, 0, -5, 11, 2],
[0, -3, -4, 0, 12, 8]]
and I want to create another array by setting half of A entries to zero i.e.
B = [[1, 2, 4, 0, -0, 0],
[3, 5, 4, 0, 0, 0],
[4, 6, 0, 0, 0, 0],
[0, -3, -4, 0, 0, 0]]
Use numpy
import numpy as np
a = np.array([[1, 2, 4, 0, -2, 6],
[3, 5, 4, 9, 10, -3],
[4, 6, 0, -5, 11, 2],
[0, -3, -4, 0, 12, 8]])
a[:, a.shape[1]//2:] = 0
print(a)
gives
array([[ 1, 2, 4, 0, 0, 0],
[ 3, 5, 4, 0, 0, 0],
[ 4, 6, 0, 0, 0, 0],
[ 0, -3, -4, 0, 0, 0]])
To convert back to a python list, use a.tolist()
You can try something like this :
One line solution:
A = [[1, 2, 4, 0, -2, 6],
[3, 5, 4, 9, 10, -3],
[4, 6, 0, -5, 11, 2],
[0, -3, -4, 0, 12, 8]]
[item.__setitem__(item.index(value), 0)for item in A for index,value in enumerate(item[len(item)//2:])]
print(A)
output:
[[1, 2, 4, 0, 0, 0], [3, 5, 4, 0, 0, 0], [4, 6, 0, 0, 0, 0], [0, -3, -4, 0, 0, 0]]
Detailed solution:
Above list comprehension is same as:
for item in A:
for index,value in enumerate(item[len(item)//2:]):
item[item.index(value)]=0
print(A)
Using list comprehension
[[0 if i>=len(l)/2 else j for i,j in enumerate(l)] for l in A]
Output:
[[1, 2, 4, 0, 0, 0],
[3, 5, 4, 0, 0, 0],
[4, 6, 0, 0, 0, 0],
[0, -3, -4, 0, 0, 0]]
Simple solution assuming all the rows are of the same length
c = len(a[0])
[row[:c/2] + [0] * (c/2) for row in a]

Categories

Resources