Gostaria de ordenar um número arbitrário de elementos em uma matriz de formato arbitrário ( matrix_a
) com base nos valores de uma matriz binária ( element_map
) que descreve os atributos dos elementos. matrix_b
define quais elements
podem ser adjacentes entre si em matrix_a
. "Adjacente", neste caso, inclui diagonal. Um exemplo concreto, mas de brinquedo:
import numpy as np
#This is a just a mask of the final matrix, to show the shape.
#The center position (5) is adjacent to all other positions whereas
#position (1) is adjacent to 4, 2 and 5, etc.
matrix_a = np.array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])
elements = range(0, 10)
#each 'row' corresponds to an element
#each 'column' corresponds to an attribute of the various elements
#here, each element has 5 attributes, which are always 0 or 1
element_map = np.array([[0, 0, 1, 0, 1], #element 1
[1, 1, 1, 0, 1], #element 2
[1, 0, 0, 1, 0], #element 3
[1, 0, 1, 0, 0], #etc.
[1, 1, 1, 0, 0],
[1, 1, 0, 1, 0],
[1, 0, 1, 0, 1],
[1, 1, 0, 0, 0],
[1, 1, 0, 0, 0]]) #element 9
A saída desejada é que os nove elements
sejam colocados matrix_a
, cada um aparecendo apenas uma vez, com base em uma regra de adjacência necessária matrix_b
. A regra é que para qualquer element
colocado em matrix_a
, o element
valor de ' para o atributo x
(pode ser qualquer atributo) deve ser zero, e o valor de todos os adjacentes (adjacentes em matrix_a
) elements
deve ser 1 para o atributo x
.
Suponha também que temos uma função que pode aceitar uma matriz e coordenadas e retornar todos os valores adjacentes:
def adj(m, x, y):
''' find adjacent values to coordinates x,y in a matrix, m'''
if x == 0:
if y == 0:
r = m[0:2,0:2] # x==0, y==0
else:
r = m[0:2,y-1:y+2] # x==0, y!=0
elif y == 0: # x != 0, y == 0
r = m[x-1:x+2,0:2]
else: #any other position
r = m[(x-1):(x+2),(y-1):(y+2)]
return [i for i in r.flatten().tolist() if i != m[x,y]]
#example:
q = np.arange(1,97).reshape(12,8).T
array([[ 1, 9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89],
[ 2, 10, 18, 26, 34, 42, 50, 58, 66, 74, 82, 90],
[ 3, 11, 19, 27, 35, 43, 51, 59, 67, 75, 83, 91],
[ 4, 12, 20, 28, 36, 44, 52, 60, 68, 76, 84, 92],
[ 5, 13, 21, 29, 37, 45, 53, 61, 69, 77, 85, 93],
[ 6, 14, 22, 30, 38, 46, 54, 62, 70, 78, 86, 94],
[ 7, 15, 23, 31, 39, 47, 55, 63, 71, 79, 87, 95],
[ 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96]])
adj(q, 5, 5)
[37, 45, 53, 38, 54, 39, 47, 55]
Como observação lateral, o espaço de busca possível para uma matriz 8x12 e 96 elementos é 10**149 = 96!
Talvez isso fosse um trabalho para programação linear/inteira, mas não tenho certeza de como formular as restrições de adjacência
Isso pode ser feito com um ILP (Programa Linear Inteiro). Um corte bruto é mostrado abaixo. Parece que você não declarou nenhuma ponderação para considerar um resultado superior a outro, então você também pode ter sorte com uma abordagem CP (Programação de Restrições). O ILP abaixo usa o número de preenchimentos de elementos bem-sucedidos como objetivo. Pode haver muitas soluções (ou nenhuma), e apenas pegará a primeira descoberta.
É importante notar que o exemplo que você forneceu não tem solução para uma matriz de destino 3x3. Usei o segmento abaixo para criar elementos da matriz identidade (invertida 1/0), que - por inspeção - é o melhor possível para ajustar a lista de elementos.
Ajustei
adj()
um pouco sua função porque você tinha um esquema misto onde pegava (x, y) e retornava uma posição numerada.... Acabei de fazer tudo (x, y)Se houver uma solução, esta tende a encontrá-la muito rapidamente para tamanhos moderados de
matrix_a
, especialmente se o conjunto de soluções for grande.Eu usei
cbc
o solucionador aqui, que é um ótimo freeware, mas existem outros solucionadores MIP que funcionam bem (glpk, highs, ...)Ainda há alguma "carne com osso" aqui para otimizar um pouco isso, principalmente através do corte de domínio das
[e, p, a]
tuplas para atribuições legais com base noa
valor, o que significa que qualquerplace[e, p, a]
elemento em quee
tenha um atributo for diferente de zeroa
não é legal e deve ser removido do domínio paraplace
.adjacency
Então você pode pré-computar os possíveis vizinhos para qualquer elemento para reduzir um pouco o tamanho da restrição.Código:
Saída (truncada):