I have a list of possibilities and a desired input:
possibles = [20, 30, 40, 50, 60, 70, 80, 100]
desired = [20, 30, 40]
I want to generate the close by lists. Example:
# Distance of 1 (i.e. 1 element changes to a close-by)
[30, 30, 40]
[20, 40, 40]
[20, 30, 30]
[20, 30, 50]
# Distance of 2:
[40, 30, 40]
[30, 30, 50]
[30, 40, 40]
...
My current version is only varying one element at a time, thus, as soon as the distance is above 1, I'm missing a lot of combination.
def generate_close_by(possibles, desired):
for k in range(1, 4):
for i, elt in enumerate(desired):
id = possibles.index(elt)
new = desired[:]
if id < len(possibles)-k-1:
new[i] = possibles[id+k]
yield (new)
if id > k:
new[i] = possibles[id-k]
yield (new)
# Output
[30, 30, 40]
[20, 40, 40]
[20, 30, 50]
[20, 30, 30]
[40, 30, 40]
[20, 50, 40]
[20, 30, 60]
[50, 30, 40]
[20, 60, 40]
[20, 30, 70]
I'm quite sure a module should already exist to do this kind of iteration (itertools?), could you point me to the write function?
Thanks.
EDIT:
Update on the tries...
I'm trying to generate a list the same size of desired in which each element correspond to how much I have to move the element of desired.
desired = [20, 30, 40]
# Distance of 1:
distance = [1, 0, 0]
distance = [0, 1, 0]
distance = [0, 0, 1]
distance = [-1, 0, 0]
distance = [0, -1, 0]
distance = [0, 0, -1]
And then plan was to try to create the new list, and if it can't (out of bounds), it just continues. Not working yet, but might be a good approach.
I guess I will show a more long winded approach that can be more easily generalizable.
I first write down the problem.
possible_pts = [20, 30, 40, 50, 60, 70, 80, 100]
starting_pt_in_idx = [0, 1, 2]
distance = 2
There are 3 axes that can "change". I first find the combinations of axis changes.
N = len(starting_pt_in_idx)
axis = list(range(N))
import itertools
axismoves = list(itertools.combinations_with_replacement(axis, distance))
print(axismoves)
Next, we bin it. For example, if I see axis-0 appearing twice, it becomes [2,0,0].
abs_movements = []
for combi in axismoves:
move_bin = [0] * N
for i in combi:
move_bin[i] += 1
abs_movements.append(move_bin)
print(abs_movements)
The above gave the absolute movements. To find the actual movement, we must take into consideration the change can be positive or negative along that axis.
import copy
actual_movements = []
for movement in abs_movements:
actual_movements.append(movement)
for i, move in enumerate(movement):
if move != 0:
_movement = copy.deepcopy(movement)
_movement[i] = - move
actual_movements.append(_movement)
print(actual_movements)
The final step is to translate the index into actual positions. So first we write this helper function.
def translate_idx_to_pos(idx_vect, points):
idx_bounds = [0, len(points) - 1]
pos_point = [0] * len(idx_vect)
for i, idx_pos in enumerate(idx_vect):
if idx_pos < idx_bounds[0] or idx_pos > idx_bounds[1]:
return None
else:
pos_point[i] = points[idx_pos]
return pos_point
Using the actual movements to act on the starting point index, then translating it back to the positions.
from operator import add
final_pts = []
for movement in actual_movements:
final_pt_in_idx = list(map(add, starting_pt_in_idx, movement))
final_point = translate_idx_to_pos(final_pt_in_idx, possible_pts)
if final_point is not None:
final_pts.append(final_point)
print(final_pts)
This gives
[40, 30, 40]
[30, 40, 40]
[30, 20, 40]
[30, 30, 50]
[30, 30, 30]
[20, 50, 40]
[20, 40, 50]
[20, 20, 50]
[20, 40, 30]
[20, 30, 60]
[20, 30, 20]