Pythonic way to check diagonals in nested list - python

I have a nested list of 0s and ones such as :
L = [[1, 0, 1],
[0, 0, 0],
[0, 0, 1]]
I want to check if any ones are diagonal to each other. The nested list can come in any size, as long as the length of the sublists is equal to the length of the whole list (it's a square). So if I ran it on the above list, it would return False, because of two ones being diagonal.
My current code is:
for num, i in enumerate(List):
for count, m in enumerate(i):
key1 = True
key2 = True
key3 = True
key4 = True
num1 = num
num2 = num
num3 = num
num4 = num
count1 = count
count2 = count
count3 = count
count4 = count
if m == 1:
while key1 or key2 or key3 or key4:
#print(key1, key2, key3, key4)
try:
if List[num1 + 1][count1 + 1] == 1:
print(List[num1 + 1][count1 + 1])
return False
num1 += 1
count1 += 1
except IndexError:
key1 = False
try:
if List[num2 - 1][count2 + 1] == 1:
if num2 > 0:
print(List[num2 - 1][count2 + 1])
return False
num2 -= 1
count2 += 1
except IndexError:
key2 = False
try:
if List[num3 + 1][count3 - 1] == 1:
if count3 > 0:
print(List[num3 + 1][count3 - 1])
print(num3 + 1, count3 - 1)
return False
num3 += 1
count3 -= 1
except IndexError:
key3 = False
try:
if List[num4 - 1][count4 - 1] == 1:
if count4 > 0 and num4 > 0:
print(List[num4 - 1][count4 - 1])
return False
num4 -= 1
count4 -=1
except IndexError:
key4 = False
return True
The code scans the list for 1s, and when one is found, it looks at its four corners and searches them. It continues to search in the direction of the corner, moving one grid at a time. If another 1 is found, it returns false. Once all possible diagonal grids are searched (up to returning index error), the code moves to the next 1 that is found. Once every single 1 is searched and none of them find any diagonal is found, the code returns true.
It feels clunky and inefficient, but I'm not sure how to compact it. Is there a shorter way to do this?

You can access the diagonals like so:
L = [[1, 0, 1],
[0, 0, 0],
[0, 0, 1]]
diag1 = [L[i][i] for i in range(len(L))]
diag2 = [L[i][len(L[0])-i-1] for i in range(len(L))]
if diag1 == diag2:
pass #do something here

You can start by thinking of a means of naming the diagonals. For example, in a 3x3 matrix the indices (x, y) go like this:
(0,0) (1,0) (2,0)
(0,1) (1,1) (2,1)
(0,2) (1,2) (2,2)
If we follow the diagonals, their indices have a simple pattern. The diagonals that run lower left to upper right are:
Diag #1 (0,0)
Diag #2 (0,1) (1,0)
Diag #3 (0,2) (1,1) (2,0)
Diag #4 (1,2) (2,1)
Diag #5 (2,2)
From upper left to lower right they are:
#1 (0,2)
#2 (0,1) (1,2)
#3 (0,0) (1,1) (2,2)
#4 (1,0) (2,1)
#5 (2,0)
Notice that in the lower left to upper right case, every cell (x, y) on the same diagonal has the same value of x + y. In the other case, every cell on the same diagonal has the same value of x - y. Obviously this will be true for any size matrix. We can use x+y and x-y to name the individual diagonals. As we step through the matrix, each time we encounter a "1" we can immediately calculate the names of the two diagonals that intersect there.
This suggests an efficient algorithm for deciding if any two "1"s are on the same diagonal. In Python we can use two sets to keep track of the "occupied" diagonals. If we encounter a "1" on an already-occupied diagonal, we return True, otherwise false. We can step through the matrix in any order provided we visit all the elements.
def has_diagonal_ones(a):
# a is a 2-dimensional square array of 0's and 1's
lower_left_upper_right = set()
upper_left_lower_right = set()
for x in range(len(a)):
for y in range(len(a)):
if a[x][y] == 0:
continue
i_lower_to_upper = x + y
i_upper_to_lower = x - y
if i_lower_to_upper in lower_left_upper_right:
return True
if i_upper_to_lower in upper_left_lower_right:
return True
lower_left_upper_right.add(i_lower_to_upper)
upper_left_lower_right.add(i_upper_to_lower)
return False
L = [[1, 0, 1],
[0, 0, 0],
[0, 0, 1]]
print(has_diagonal_ones(L))
>>> True

you can use numpy to solve this problem.
import numpy as np
def diagonal(arr):
d = np.diag(a)
set_len = len(set(d))
d_len = len(d)
if set_len < d_len:
return False
else:
return True
a = np.array([[1, 0, 1], [0, 0, 0], [0, 0, 1]])
print(diagonal(a))

I have a lib for this. Checkout diag and adiag functions! https://github.com/dhilst/funcyou/blob/master/funcyou/indexers.py

Related

Dynamic Programming - minimize the sum of the array

My problem is: given an array of negative and positive integers. You are given value j to jump and r to rest. After each jump, you need to rest for r steps. Moreover, you are allowed to move 1 more step forward even when you have the ability to jump. The problem is to minimize the sum of the array.
Ex.1 r = 2, j = 2, [5, 3, -4, -2, 1, 2, 3] => -4 + -2 + 3 = -3 (Jump 5, 3, Rest -4,-2, Jump 1,2, Rest 3)
Ex.2 r = 2, j = 3, [90, 91, 92, -2, 3, 55, 3] => -2 + 3 + 55 + 3 = 59 (Jump 90,91,92 Rest -2,3,55,3)
My Idea: I decided to use DP to solve this. This is my pseudocode.
def minimalSum (MIN, array, jump, rest, steps_left_to_jump, i):
if MIN[i] is not empty:
return MIN[i]
if i == len(array) - 1:
MIN[i] = array[i]
else:
if steps_left_to_jump == 0:
if i == 0:
MIN[i] = minimalSum(MIN, array, jump, rest, rest - 1, jump)
else:
if i + jump + 1 < len(array):
MIN[i] = array[i] + minimalSum(MIN, array, jump, rest, rest - 1, i + jump + 1)
o1 = array[i] + minimalSum(MIN, array, jump, rest, 0, i + 1)
if MIN[i] is not None:
if o1 < MIN[i]:
MIN[i] = o1
else:
MIN[i] = o1
else:
MIN[i] = array[i] + minimalSum(MIN, array, jump, rest, steps_left_to_jump - 1, i + 1)
return MIN[i]
MIN is array used to store best sums.
The problem that it does not work for all inputs, can you help me spot where I am going wrong. Consider the example
r = 2, j = 2 , [2 ,-2 ,-3,1 ,3 ,4]. The answer should be 1 (Visit 2, -2, Jump -3, Rest 4) 2-2-3+4 = 1, but my program outputs 5
Your problem seems to be in this line:
if i == 0:
MIN[i] = minimalSum(MIN, array, jump, rest, rest - 1, jump)
This prevents you from ever choosing Visit whenever i is 0, since you ALWAYS jump in your first step. I don't know about your full code, but this part should be:
if i == 0:
MIN[i] = min(minimalSum(MIN, array, jump, rest, rest - 1, jump) , # case where you jump at 0
array[0] + minimalSum(MIN, array, jump, rest, 0, 1) # case where you visit index 0
)
Also, your code give you an out-of-bounds error if jump>len(MIN)-1. If this condition is true, you know you should ALWAYS visit.
Given all of this, I'm going to write the recursive formula, you can then memoize it:
def opt_sum(array, r, j):
if j > len(array)-1:
return sum(array) # cannot jump, return sum of full array
else:
visit_val = array[0] + opt_sum(array[1:], r, j) # best sum if you choose to visit index 0
jump_val = (
sum(array[j:j+r]) # total mandatory resting visits
+ opt_sum(array[j+r:], r, j) # the optimal sum of the subarray
) # starting after the rest
return min(visit_val, jump_val) # return best of both policies

How do I write O(n2) program of matrix n x n?

I am practicing and trying to write O(n^2) program that tests whether there are two 1s lying on the same row or the same column in A. Where A = n x n matrix of 0s and 1s.
Given A as:
I should get answer return of 2 matches.
One is on the 1st row, and another on the 3rd column.
My 2nd Attempt:
def testLines():
count = 0
for x in range( 0, len(A)-1 ):
if( (A[x] == 1) & (A[x+1] == 1) ):
count+=1
for y in range( 0, len(A)-1):
if( (A[y] == 1 & A[y+1]) == 1 ):
count+=1
print( count, '1s has been matched in the array A')
testLines()
You want to nest the two loops and change the indexes so that both x and y are parsed. Currently your code moves through (all x, y = 0) and (x = 0, all y).
A = [[0, 0, 1, 1],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 1, 0]]
def testLines():
count = 0
N = len(A)
for x in range(N):
for y in range(N):
if A[x][y] == 1:
if x+1 < N and A[x+1][y] == 1:
count += 1
if y+1 < N and A[x][y+1] == 1:
count += 1
print(count, '1s has been matched in the array A')
testLines()
Alternatively, you can go the Schwarzenegger way and not check if (x+1, y) or (x, y+1) even exist. That will raise IndexErrors that you can choose to ignore.
def testLines():
count = 0
N = len(A)
for x in range(N):
for y in range(N):
try:
if A[x][y] == 1 and A[x+1][y] == 1 or A[x][y+1] == 1:
count += 1
except IndexError:
continue
print(count, '1s has been matched in the array A')
You can run one nested loop (n²) to get summation of rows. If summation is 2 then that row has two 1s.
Now interchange rows and columns(consider rows as columns & vice versa).
Again run nested loop (n²) to check summation of columns.
n²+n²= O(n²)

how can solve this problem with dynamic programming?

Instructions : rat can move just up or right
input:
The first line contains the number of table size n and the number of cheese m.
From the next line, the position x, y of the cheese is given
Output :
The maximum number of cheese to eat
Exemple1:
input : 1 1 1
output : 1 1
Example2:
input :
3 2
1 2
3 1
output : 1
Example 3:
input :
5 5
2 3
3 2
4 3
4 5
5 2
output: 3
how can I solve with python?
I tried
def maxAverageOfPath(table, N):
dp = [[0 for i in range(N)] for j in range(N)]
dp[0][0] = table[0][0]
# Initialize first column of total table(dp) array
for i in range(0, N):
dp[i][0] = 0
for j in range(0, N):
dp[0][j] = 0
for i in range(0, N):
for j in range(0, N):
print(i, j)
if i == N-1 and j == N-1:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
continue
if i == N-1 :
dp[i][j] = table[i][j + 1]
continue
if j == N-1 :
dp[i][j] = table[i + 1][j]
continue
dp[i][j] = max(table[i + 1][j], table[i][j + 1])
return dp
but failed...
For dynamic programming you want an edge condition(s) and a way to score where you are right now. After that it's more-or-less smart brute force. The smart part comes from memoizing so you don't repeat work.
Here's a basic recursive approach for python that does the following:
Organize table of cheese as a frozen set of tuples. This can be hashed for memoization and you can determine of a location is in the set in constant time.
Creates an edge condition for the end (when both coordinates are N) and an edge condition for when you walk off the map -- that just returns 0.
Uses lru_cache to memoize. You can implement this yourself easily.
from functools import lru_cache
def hasCheese(table, location):
''' Helper function just to improve readability '''
return 1 if location in table else 0
#lru_cache()
def maxC(table, N, location = (0, 0)):
# edge conditions final square and off the grid:
if location[0] == N and location[1] == N:
return hasCheese(table, location)
if any(l > N for l in location):
return 0
# recursion
score_here = hasCheese(table, location)
return max(score_here + maxC(table, N, (location[0] + 1, location[1])),
score_here + maxC(table, N, (location[0], location[1] + 1))
)
t = frozenset([(2, 3), (3, 2), (4, 3), (4, 5), (5, 2)])
N = 5
print(maxC(t, N))
# prints 3
If you want to do this in a top-down manner using a matrix, you need to be very careful that you always have the previous index set. It's easier to make mistakes doing it this way because you need to get the indexes and order just right. When you set it up as two nested increasing loops, that means the next value is always the current cell plus the max of the two cells one unit less — you should always be looking backward in the matrix. It's not clear what you are trying to do when you are looking forward with this:
dp[i][j] = table[i][j + 1]
because j+1 has not been determined yet.
Since the cheese coordinates are 1 indexed, an easy way forward is to make your matrix zero indexed and N+1 in size. Then when you start your for loops at 1 you can always look and at lower index without undershooting the matrix and avoid a lot of the if/else logic. For example:
def hasCheese(table, location):
''' Helper function just to improve readability '''
return 1 if location in table else 0
def maxAverageOfPath(table, N):
# matrix is sized one unit bigger
dp = [[0 for i in range(N+1)] for j in range(N+1)]
# iterate 1-5 inclusive
for i in range(1, N+1):
for j in range(1, N+1):
# because the zeroth row and column are already zero, this works without undershooting the table
dp[i][j] = hasCheese(table, (i, j)) + max(dp[i][j-1], dp[i-1][j])
# solution is in the corner
return dp[N][N]
t = {(2, 3), (3, 2), (4, 3), (4, 5), (5, 2)}
N = 5
print(maxAverageOfPath(t, N)) #3
When you'r done your matrix will look like:
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 1, 1]
[0, 0, 1, 1, 1, 1]
[0, 0, 1, 2, 2, 3]
[0, 0, 2, 2, 2, 3]
Your starting point is at (1, 1) starting in the top-right and your answer is the bottom left corner.
At each point you have two options to move further :
array [row] [col+1]
array [row+1] [col]
As we have to find out a path which involves max cheese.
It can be achived by recurring through the array like below to solve the same:
Solution =>
array [i] [j] + Max(Recur(array [i] [j+1]), Recur(array [i+1] [j]));

How to choose specific minimum values in lists and do mathematical operations on them

After getting data from user input I put the input in lists like this:
x= [3, 2, 1, 0, 1, 2]
y= [1, 2, 0, 3, 4, 1]
I have manged to write this as:
rows = 3
weight = 0
high =0
low =0
while rows>=3 and rows<=200:
rows, weight = map(int, input().split())
break
count_input = 0
while count_input<rows:
while high>=0 and low<=100:
high, low = map(int, input().split())
i=i+1
if count_input==rows:
break
To choose the minimum number in a list i tried this:
smallest = None
for number in [1, 0, 3, 4, 5, 2]:
if smallest is None or number < smallest:
smallest = number
print('Smallest:', smallest)
my questions are:
How to determine minimum values in these two lists and add minimum values together BUT taking into account that selected minimum values of same positions like x[0] and y[0], or x[1] and y[1] can not be added together.
Elements in diagonal position to each other like x[0] and y[1], x[2] and y[3] can not be added together
Also How to put a limit for number of chosen values, like choosing the minimum 4 values found in lists together
This is how I would approach finding the minimum of the data set, with the logic for not using values between the lists if they have the same index or a diagonal index
x = [3, 2, 1, 0, 1, 2]
y = [1, 2, 0, 3, 4, 1]
final_min = max(x) + max(y)
for x_index in range(0, len(x)):
for y_index in range(0, len(y)):
if y_index == x_index - 1 or y_index == x_index or y_index == x_index + 1:
pass
else:
test_min = x[x_index] + y[y_index]
if test_min < final_min:
print(test_min) # prints 3, 2, 1
final_min = test_min
print(final_min) # prints 1
This makes sense by visually looking at the data, as there are 3 places that the sum would be 1, and the only place it could be smaller (0) would be 0 + 0 but that is a diagonal pair so it cannot be included. You should keep in mind that this is a computationally expensive approach though because it iterates through the y list for every index in the x list, and if your lists are large this will take a LONG time! Also, if the lists are different lengths then the program will likely hit an IndexError and I have not included safeguards for that. I cannot help you on your final point because I do not understand what is meant by:
How to put a limit for number of chosen values, like choosing the minimum 4 values found in lists together
You would need to clarify for anybody to understand what is meant here.
Use min(..) and index(..).
This solution may not be entirely correct, but you get the idea...
def add_min_with_constraints(a, b):
if len(a) == 0 or len(b) == 0:
return -math.inf
min_a_i = a.index(min(a))
min_b_i = b.index(min(b))
if min_a_i == min_b_i or abs(min_a_i - min_b_i) == 1: # same or diagonal indices
# exclude the current minimums
return add_min_with_constraints(a[:min_a_i] + a[min_a_i+1:],
b[:min_b_i] + b[min_b_i+1:])

Programming Maze Solution recursively

This function is intended to recursively navigate a maze and find the length of the shortest path. The path itself is not necessary, only the length. The maze is represented by a 2d list with values such as
0 1 0 0 0
0 0 0 1 0
0 0 0 1 0
The user starts at (0,0) and must end up at the end of the maze as defined (in my case it is the bottom right cell). 1's represent walls.
def maze(x,y,array,length):
m = len(array)
n = len(array[0])
if x < 0 or y < 0 or x == m or y == n or array[x][y] == 1:
return float("inf")
elif x == m - 1 and y == n - 1:
return length
else:
array[x][y] = 1
up = maze(x - 1,y,array,length + 1)
right = maze(x,y + 1,array,length + 1)
down = maze(x + 1,y,array,length + 1)
left = maze(x,y - 1,array,length + 1)
return min(up,down,left,right)
array = [[0,1,0,0,0],[0,0,0,1,0],[0,0,0,1,0]]
minLength = maze(0,0,array,1)
print(minLength)
I designed it so that it recursively finds all possible paths from each direction (up, down, left and right), and returns the lowest value from all these paths with each step of the way. It returns inf for any path that is not valid.
For this specific array, it returns 11, which is false, it should be 9. I do not believe it is merely a mathematical error, as I tried printing each step of the way and it is not recognizing certain paths (it returns inf for paths that most definitely have options).
I can't seem to find where my code is going wrong, it seems like it should properly return the value, but in practice it does not.
array is a reference to the original array, not a local copy. See any of the on-line tutorials on how Python passes function arguments, or how it handles lists. You can see the effect by printing array in your main program after the call to maze:
Final Maze [
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 0]
]
Fixing this is relatively easy: copy the nested list and use that copy locally.
from copy import deepcopy
def maze(x,y,array,length):
m = len(array)
n = len(array[0])
if x < 0 or y < 0 or x == m or y == n or array[x][y] == 1:
return float("inf")
elif x == m - 1 and y == n - 1:
return length
else:
new_maze = deepcopy(array)
new_maze[x][y] = 1
up = maze(x - 1,y,new_maze,length + 1)
right = maze(x,y + 1,new_maze,length + 1)
down = maze(x + 1,y,new_maze,length + 1)
left = maze(x,y - 1,new_maze,length + 1)
return min(up,down,left,right)
array = [[0,1,0,0,0],[0,0,0,1,0],[0,0,0,1,0]]
minLength = maze(0,0,array,1)
print("Final Maze", array)
print(minLength)
The output from this is (edited for readability again)
Final Maze [
[0, 1, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 1, 0]
]
9

Categories