Related
Is it possible to write a Python program to find three numbers whose sum and product are given by the user? For example, 3 + 3 + 5 = 11 and 3 × 3 × 5 = 45, so if I give it 11 and 45, it should return (3, 3, 5).
The caveman approach (brute force with a miniscule amount of dynamic programming). MiTriPy's answer generalizes to n variables, although probably isn't more performant.
def solve(eqsum, eqprod):
solution_count = 0
for x in range(1, eqsum+1):
for y in range(1, eqsum+1-x):
for z in range(1, eqsum+1-x-y):
if x+y+z == eqsum:
if x*y*z == eqprod:
print(f"x={x} y={y} z={z}")
solution_count += 1
print(f"Found {solution_count} solutions.");
solve(eqsum=11, eqprod=45)
Made something fast and very clumsy:
import itertools
from numpy import prod
def find_subset_of_numbers(number, product):
subset_of_numbers = [x for x in range(1, number + 1)]
for x in range(1, number+1):
subset_of_numbers.append(x)
for x in range(1, number+1):
subset_of_numbers.append(x)
result = [seq for i in range(3, 0, -1)
for seq in itertools.combinations(subset_of_numbers, i)
if sum(seq) == number and len(seq) == 3 and prod(seq) == product]
return result
This will not handle duplicates very well but you could add another check for that:
print(find_subset_of_numbers(11, 45))
output: [(3, 5, 3), (3, 5, 3), (3, 3, 5), (3, 3, 5), (3, 5, 3), (3, 3, 5), (5, 3, 3), (3, 5, 3), (3, 3, 5)]
Here is a fairly efficient solution.
from collections.abc import Iterator
from math import sqrt
def solve(target_sum: int, target_prod: int) -> Iterator[tuple[int, int, int]]:
for x in range(1, target_sum):
if target_prod % x:
continue
midpoint = (target_sum - x) / 2
try:
radius = sqrt(midpoint ** 2 - target_prod // x)
except ValueError:
continue
y = midpoint + radius
if y.is_integer():
y = int(y)
z = int(midpoint - radius)
yield (x, y, z)
yield (x, y, z)
It uses the fact that if x + y = 2m and xy = p, then {x, y} = {m ± √(m² − p)}.
Let's say I have a two dimensional grid of 10x10 cells. The top left cell has coordinates (0,0) and
the bottom right cell has coordinates (9,9).
The code below doesn't seem to function the way I want it to.
I can't figure out what I am doing wrong.
'''
X = 10
Y = 10
class Cell:
def __init__(self,x,y) -> None:
self.coordinates = (x,y)
self.neigbors = self.find_neighbors()
def find_neighbors(self):
x,y = self.coordinates
neighbors = [
(x+1,y),(x-1,y),(x,y+1),(x,y-1),(x+1,y+1),
(x+1,y-1),(x-1,y+1),(x-1,y-1)
]
for neighbor in neighbors:
if neighbor[0] < 0 or neighbor[1] < 0:
neighbors.remove(neighbor)
elif neighbor[0] >= X or neighbor[1] >= Y:
neighbors.remove(neighbor)
return neighbors
cell1 = Cell(0,0)
cell1.neigbors
# [(1, 0), (0, 1), (1, 1), (-1, 1)]
# shouldn't have (-1,1)
cell2 = Cell(9,9)
cell2.neigbors
# [(8, 9), (9, 8), (10, 8), (8, 8)]
# shouldn't have (10,8)
'''
Better not to remove items from a list while iterating over it (as already pointed out in the comments). Here's an idea where you mark the entries in the list of potential coordinates as "unwanted" then subsequently reconstruct the desired output:
MX = 10
MY = 10
def neighbours(x, y):
pn = [(x-1, y), (x+1, y), (x-1, y-1), (x, y-1),
(x+1, y-1), (x-1, y+1), (x, y+1), (x+1, y+1)]
for i, t in enumerate(pn):
if t[0] < 0 or t[1] < 0 or t[0] >= MX or t[1] >= MY:
pn[i] = None
return [c for c in pn if c is not None]
print(neighbours(5, 4))
Instead of the for-loop put this piece.
neighbors = [neighbor for neighbor in neighbors if validate_cell(neighbor)]
The function validate_cell(coordinate).
validate_cell(coordinate):
if coordinate[0] < 0 or coordinate[1] < 0:
return False
elif coordinate[0] >= X or coordinate[1] >= Y:
return False
else:
return True
I want to iterate over pairs of integers in order of the sum of their absolute values. The list should look like:
(0,0)
(-1,0)
(0,1)
(0,-1)
(1,0)
(-2,0)
(-1,1)
(-1,-1)
(0,2)
(0,-2)
(1,1)
(1,-1)
(2,0)
[...]
For pairs with the same sum of absolute values I don't mind which order they come in.
Ideally I would like to be able to create the pairs forever so that I can use each one in turn. How can you do that?
For a fixed range I can make the list of pairs in an ugly way with:
sorted([(x,y)for x in range(-20,21)for y in range(-20,21)if abs(x)+abs(y)<21],key=lambda x:sum(map(abs,x))
This doesn't allow me to iterate forever and it also doesn't give me one pair at a time.
This seems to do the trick:
from itertools import count # Creates infinite iterator
def abs_value_pairs():
for absval in count(): # Generate all possible sums of absolute values
for a in range(-absval, absval + 1): # Generate all possible first values
b = abs(a) - absval # Compute matching second value (arbitrarily do negative first)
yield a, b
if b: # If first b is zero, don't output again, otherwise, output positive b
yield a, -b
This runs forever, and operates efficiently (avoiding recomputing anything unnecessarily).
This will do it. If you really want it to be infinite, remove the firs if statement.
import itertools
def makepairs(count=3):
yield (0,0)
for base in itertools.count(1):
if base > count: # optional escape
return # optional escape
for i in range(base+1):
yield (i, base-i)
if base != i:
yield (i, i-base)
if i:
yield (-i, base-i)
if base != i:
yield (-i, i-base)
print(list(makepairs(9)))
The solution below produces a sum stream with tuples of any length:
from itertools import count
def pairs(l = 2):
def groups(d, s, c = []):
if not d and sum(map(abs, c)) == s:
yield tuple(c)
elif d:
for i in [j for k in d[0] for j in {k, -1*k}]:
yield from groups(d[1:], s, c +[i])
for i in count():
yield from groups([range(i+1) for _ in range(l)], i)
p = pairs()
for _ in range(10):
print(next(p))
You could make an infinite generator function:
def pairSums(s = 0): # base generation on target sum to get pairs in order
while True: # s parameter allows starting from a given sum
for i in range(s//2+1): # partitions
yield from {(i,s-i),(s-i,i),(i-s,-i),(-i,i-s)} # no duplicates
s += 1 # next target sum
Output:
for p in pairSums(): print(p)
(0, 0)
(0, 1)
(0, -1)
(1, 0)
(-1, 0)
(2, 0)
(-2, 0)
(0, -2)
(0, 2)
(1, 1)
(-1, -1)
(3, 0)
(0, 3)
(0, -3)
(-3, 0)
(1, 2)
(-1, -2)
(2, 1)
...
First notice that you can lay your totals out in a grid for non-negative values:
x
3|3
2|23
1|123
0|0123
-+----
|0123y
Here we can see a pattern where the diagonals are your totals. Let's just trace some systematic line through them. The following shows an order you could walk through them:
x
3|6
2|37
1|148
0|0259
-+----
|0123y
Here the matrix contains the order of the iterations.
This solves your problem for non-negative values of x and y. To get the rest you can just negate x and y, making sure you don't do it for when they are zero. Something like this:
def generate_triplets(n):
yield 0, (0, 0)
for t in range(1, n + 1): # Iterate over totals t
for x in range(0, t + 1): # Iterate over component x
y = t - x # Calclulate component y
yield t, (x, y) # Default case is non-negative
if y > 0:
yield t, (x, -y)
if x > 0:
yield t, (-x, y)
if x > 0 and y > 0:
yield t, (-x, -y)
def generate_pairs(n):
yield from (pair for t, pair in generate_triplets(n))
# for pair in generate_pairs(10):
# print(pair)
for t, (x, y) in generate_triplets(3):
print(f'{t} = abs({x}) + abs({y})')
This outputs
0 = abs(0) + abs(0)
1 = abs(0) + abs(1)
1 = abs(0) + abs(-1)
1 = abs(1) + abs(0)
1 = abs(-1) + abs(0)
2 = abs(0) + abs(2)
2 = abs(0) + abs(-2)
2 = abs(1) + abs(1)
2 = abs(1) + abs(-1)
2 = abs(-1) + abs(1)
2 = abs(-1) + abs(-1)
2 = abs(2) + abs(0)
2 = abs(-2) + abs(0)
3 = abs(0) + abs(3)
3 = abs(0) + abs(-3)
3 = abs(1) + abs(2)
3 = abs(1) + abs(-2)
3 = abs(-1) + abs(2)
3 = abs(-1) + abs(-2)
3 = abs(2) + abs(1)
3 = abs(2) + abs(-1)
3 = abs(-2) + abs(1)
3 = abs(-2) + abs(-1)
3 = abs(3) + abs(0)
3 = abs(-3) + abs(0)
Or as pairs:
(0, 0)
(0, 1)
(0, -1)
(1, 0)
(-1, 0)
(0, 2)
(0, -2)
(1, 1)
(1, -1)
(-1, 1)
(-1, -1)
(2, 0)
(-2, 0)
...
For each sum, walk the diagonal in one quadrant and rotate each coordinate into the other quadrants:
from itertools import count
def coordinates():
yield 0, 0
for sum in count(1):
for x in range(sum):
y = sum - x
yield x, y
yield y, -x
yield -x, -y
yield -y, x
(I hope I understood the requirements) I used itertools product:
>>> for i in sorted(itertools.product(range(-5, 4), range(-5, 4)), key=lambda tup: abs(tup[0]) + abs(tup[1])):
print(i)
...
(0, 0)
(-1, 0)
(0, -1)
(0, 1)
(1, 0)
(-2, 0)
(-1, -1)
(-1, 1)
(0, -2)
(0, 2)
(1, -1)
(1, 1)
(2, 0)
(-3, 0)
(-2, -1)
(-2, 1)
(-1, -2)
(-1, 2)
(0, -3)
(0, 3)
(1, -2)
(1, 2)
(2, -1)
...
I have an array that looks like this:
[['A0' 'B0' 'C0']
['A1' 'B1' 'C1']
['A2' 'B2' 'C2']]
I would like to get B1's neighbors which are B0 , C1 , B2 , A1, along with their indices.
This is what I came up with:
import numpy as np
arr = np.array([
['A0','B0','C0'],
['A1','B1','C1'],
['A2','B2','C2'],
])
def get_neighbor_indices(x,y):
neighbors = []
try:
top = arr[y - 1, x]
neighbors.append((top, (y - 1, x)))
except IndexError:
pass
try:
bottom = arr[y + 1, x]
neighbors.append((bottom, (y + 1, x)))
except IndexError:
pass
try:
left = arr[y, x - 1]
neighbors.append((left, (y, x - 1)))
except IndexError:
pass
try:
right = arr[y, x + 1]
neighbors.append((right, (y, x + 1)))
except IndexError:
pass
return neighbors
This will return a list of tuples (value, (y, x)).
Is there a better way to do this without relying on try/except?
You can do this directly in numpy without any exceptions, since you know the sizes of your array. The indices of the immediate neighbors of x, y are given by
inds = np.array([[x, y]]) + np.array([[1, 0], [-1, 0], [0, 1], [0, -1]])
You can easily make a mask that indicates which indices are valid:
valid = (inds[:, 0] >= 0) & (inds[:, 0] < arr.shape[0]) & \
(inds[:, 1] >= 0) & (inds[:, 1] < arr.shape[1])
Now extract the values that you want:
inds = inds[valid, :]
vals = arr[inds[:, 0], inds[:, 1]]
The simplest return value would be inds, vals, but if you insisted on keeping your original format, you could transform it into
[v, tuple(i) for v, i in zip(vals, inds)]
Addendum
You can easily modify this to work on arbitrary dimensions:
def neighbors(arr, *pos):
pos = np.array(pos).reshape(1, -1)
offset = np.zeros((2 * pos.size, pos.size), dtype=np.int)
offset[np.arange(0, offset.shape[0], 2), np.arange(offset.shape[1])] = 1
offset[np.arange(1, offset.shape[0], 2), np.arange(offset.shape[1])] = -1
inds = pos + offset
valid = np.all(inds >= 0, axis=1) & np.all(inds < arr.shape, axis=1)
inds = inds[valid, :]
vals = arr[tuple(inds.T)]
return vals, inds
Given an N dimensional array arr and N elements of pos, you can create the offsets by just setting each dimension sequentially to 1 or -1. The computation of the mask valid is greatly simplified by broadcasting together inds and arr.shape, as well as calling np.all across each N-sized row instead of doing it manually for each dimension. Finally, the conversion tuple(inds.T) turns inds into an actual fancy index by assigning each column to a separate dimension. The transpose is necessary becaue arrays iterate over rows (dim 0).
You can use this:
def get_neighbours(inds):
places = [(-1, 0), (1, 0), (0, -1), (0, 1)]
return [(arr[x, y], (y, x)) for x, y in [(inds[0] + p[0], inds[1] + p[1]) for p in places] if x >= 0 and y >= 0]
get_neighbours(1, 1)
# OUTPUT [('B0', (1, 0)), ('B2', (1, 2)), ('A1', (0, 1)), ('C1', (2, 1))]
get_neighbours(0, 0)
# OUTPUT [('A1', (0, 1)), ('B0', (1, 0))]
How about this?
def get_neighbor_indices(x,y):
return ( [(arr[y-1,x], (y-1, x))] if y>0 else [] ) + \
( [(arr[y+1,x], (y+1, x))] if y<arr.shape[0]-1 else [] ) + \
( [(arr[y,x-1], (y, x-1))] if x>0 else [] ) + \
( [(arr[y,x+1], (y, x+1))] if x<arr.shape[1]-1 else [] )
I have a list of lists, something like
[[1, 2, 3,],[4, 5, 6,],[7, 8, 9]].
Represented graphically as:
1 2 3
4 5 6
7 8 9
I'm looking for an elegant approach to check the value of neighbours of a cell, horizontally, vertically and diagonally. For instance, the neighbours of [0][2] are [0][1], [1][1] and [1][2] or the numbers 2, 5, 6.
Now I realise, I could just do a bruteforce attack checking every value a la:
[i-1][j]
[i][j-1]
[i-1][j-1]
[i+1][j]
[i][j+1]
[i+1][j+1]
[i+1][j-1]
[i-1][j+1]
But thats easy, and I figured I can learn more by seeing some more elegant approaches.
# Size of "board"
X = 10
Y = 10
neighbors = lambda x, y : [(x2, y2) for x2 in range(x-1, x+2)
for y2 in range(y-1, y+2)
if (-1 < x <= X and
-1 < y <= Y and
(x != x2 or y != y2) and
(0 <= x2 <= X) and
(0 <= y2 <= Y))]
>>> print(neighbors(5, 5))
[(4, 4), (4, 5), (4, 6), (5, 4), (5, 6), (6, 4), (6, 5), (6, 6)]
I don't know if this is considered clean, but this one-liner gives you all neighbors by iterating over them and discarding any edge cases.
Assuming you have a square matrix:
from itertools import product
size = 3
def neighbours(cell):
for c in product(*(range(n-1, n+2) for n in cell)):
if c != cell and all(0 <= n < size for n in c):
yield c
Using itertools.product and thanks to Python's yield expression and star operator, the function is pretty dry but still readable enough.
Given a matrix size of 3, you can then (if needed) collect the neighbours in a list:
>>> list(neighbours((2,2)))
[(1, 1), (1, 2), (2, 1)]
What the function does can be visualized as follows:
mb...
from itertools import product, starmap
x, y = (8, 13)
cells = starmap(lambda a,b: (x+a, y+b), product((0,-1,+1), (0,-1,+1)))
// [(8, 12), (8, 14), (7, 13), (7, 12), (7, 14), (9, 13), (9, 12), (9, 14)]
print(list(cells)[1:])
for x_ in range(max(0,x-1),min(height,x+2)):
for y_ in range(max(0,y-1),min(width,y+2)):
if (x,y)==(x_,y_): continue
# do stuff with the neighbours
>>> a=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> width=height=3
>>> x,y=0,2
>>> for x_ in range(max(0,x-1),min(height,x+2)):
... for y_ in range(max(0,y-1),min(width,y+2)):
... if (x,y)==(x_,y_): continue
... print a[x_][y_]
...
2
5
6
If someone is curious about alternative way to pick direct (non-diagonal) neighbors, here you go:
neighbors = [(x+a[0], y+a[1]) for a in
[(-1,0), (1,0), (0,-1), (0,1)]
if ( (0 <= x+a[0] < w) and (0 <= y+a[1] < h))]
There's no cleaner way to do this. If you really want you could create a function:
def top(matrix, x, y):
try:
return matrix[x][y - 1];
except IndexError:
return None
Here is your list:
(x - 1, y - 1) (x, y - 1) (x + 1, y - 1)
(x - 1, y) (x, y) (x + 1, y)
(x - 1, y + 1) (x, y + 1) (x + 1, y + 1)
So the horizontal neighbors of (x, y) are (x +/- 1, y).
The vertical neighbors are (x, y +/- 1).
Diagonal neighbors are (x +/- 1, y +/- 1).
These rules apply for an infinite matrix.
To make sure the neighbors fit into a finite matrix, if the initial (x, y) is at the edge, just apply one more restriction to the coordinates of neighbors - the matrix size.
>>> import itertools
>>> def sl(lst, i, j):
il, iu = max(0, i-1), min(len(lst)-1, i+1)
jl, ju = max(0, j-1), min(len(lst[0])-1, j+1)
return (il, iu), (jl, ju)
>>> lst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> tup = 0, 2
>>> [lst[i][j] for i, j in itertools.product(*sl(lst, *tup)) if (i, j) != tup]
[2, 5, 6]
I don't know how elegant it seems to you, but it seems to work w/o any hard-coding.
This generates all indices:
def neighboring( array ):
nn,mm = len(array), len(array[0])
offset = (0,-1,1) # 0 first so the current cell is the first in the gen
indices = ( (i,j) for i in range(nn) for j in range(mm) )
for i,j in indices:
all_neigh = ( (i+x,j+y) for x in offset for y in offset )
valid = ( (i,j) for i,j in all_neigh if (0<=i<nn) and (0<=j<mm) ) # -1 is a valid index in normal lists, but not here so throw it out
yield valid.next(), valid ## first is the current cell, next are the neightbors
for (x,y), neigh in neighboring( l ):
print l[x][y], [l[x][y] for x,y in neigh]
If lambdas daunt you here you are .But lambdas make your code look clean.#johniek_comp has a very clean solution TBH
k,l=(2,3)
x = (0,-1,+1)
y = (0,-1,+1)
cell_u = ((k+a,l+b) for a in x for b in y)
print(list(cell_u))
Inspired by one of the previous answers.
You can use min() and max() functions to shorten the calculations:
width = 3
height = 3
[(x2, y2) for x2 in range(max(0, x-1), min(width, x+2))
for y2 in range(max(0, y-1), min(height, y+2))
if (x2, y2) != (x, y)]
Thank you to #JS_is_bad for a great hint about the neighbors. Here is the running code for this problem:
def findNeighbours(l,elem):
#This try is for escaping from unbound error that happens
#when we try to iterate through indices that are not in array
try:
#Iterate through each item of multidimensional array using enumerate
for row,i in enumerate(l):
try:
#Identifying the column index of the givem element
column=i.index(elem)
except ValueError:
continue
x,y=row,column
# hn=list(((x,y+1),(x,y-1))) #horizontal neighbours=(x,y+/-1)
# vn=list(((x+1,y),(x-1,y))) #vertical neighbours=(x+/-1,y)
# dn=list(((x+1,y+1),(x-1,y-1),(x+1,y-1),(x-1,y+1))) #diagonal neighbours=(x+/-1,y+/-1)
#Creating a list with values that are actual neighbors for the extracted index of array
neighbours=[(x,y+1),(x,y-1),(x+1,y),(x-1,y),(x+1,y+1),(x-1,y-1),(x+1,y-1),(x-1,y+1)]
#Creating a universe of indices from given array
index_list=[(i,j) for i in range(len(l)) for j in range(len(l[i]))]
#Looping through index_list and nested loop for neighbours but filter for matched ones
# and extract the value of respective index
return_values=[l[index[0]][index[1]] for index in index_list for neighbour in neighbours if index==neighbour]
return return_values,neighbours
except UnboundLocalError:
return []
Inspired by johniek's answer here is my solution which also checks for boundaries.
def get_neighbours(node, grid_map):
row_index, col_index = node
height, width = len(grid_map), len(grid_map[0])
cells = list(starmap(lambda a, b: (row_index + a, col_index + b), product((0, -1, +1), (0, -1, +1))))
cells.pop(0) # do not include original node
cells = list(filter(lambda cell: cell[0] in range(height) and cell[1] in range(width), cells))
return cells
def numCells(grid):
x=len(grid)
y=len(grid[0])
c=0
for i in range(x):
for j in range(y):
value_=grid[i][j]
f=1
for i2 in range(max(0,i-1),min(x,i+2)):
for j2 in range(max(0,j-1),min(y,j+2)):
if (i2,j2) != (i,j) and value_<=grid[i2][j2]:
flag=0
break
if flag ==0:
break
else:
c+=1
return c
def getNeighbors(matrix: list, point: tuple):
neighbors = []
m = len(matrix)
n = len(matrix[0])
x, y = point
for i in range (x -1, x +2): #prev row to next row
for j in range(y - 1, y +2): #prev column to next col
if (0 <= i < m) and (0 <= j < n):
neighbors.append((i,j))
return neighbors
maybe you are checking a sudoku box. If the box is n x n and current cell is (x,y) start checking:
startingRow = x / n * n;
startingCol = y/ n * n