AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 77423938
Accepted
Ryan
Ryan
Asked: 2023-11-05 05:49:47 +0800 CST2023-11-05 05:49:47 +0800 CST 2023-11-05 05:49:47 +0800 CST

Ordenar elementos em uma matriz com base em outra matriz

  • 772

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_bdefine quais elementspodem 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 elementssejam 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 elementcolocado em matrix_a, o elementvalor de ' para o atributo x(pode ser qualquer atributo) deve ser zero, e o valor de todos os adjacentes (adjacentes em matrix_a) elementsdeve 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

python
  • 1 1 respostas
  • 54 Views

1 respostas

  • Voted
  1. Best Answer
    AirSquid
    2023-11-05T12:18:56+08:002023-11-05T12:18:56+08:00

    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 cbco 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 no avalor, o que significa que qualquer place[e, p, a]elemento em que etenha um atributo for diferente de zero anão é legal e deve ser removido do domínio para place. adjacencyEntão você pode pré-computar os possíveis vizinhos para qualquer elemento para reduzir um pouco o tamanho da restrição.

    Código:

    """
    fit elements into matrix based on adjacency rules
    """
    
    import numpy as np
    import pyomo.environ as pyo
    
    
    class Element:
        """a convenience to hold the rows of attribute values"""
        def __init__(self, row):
            self.attributes = tuple(row)
    
        def attribute(self, idx):
            return self.attributes[idx]
    
        def __repr__(self):
            return str(self.attributes)
    
    
    class Position:
        """a convenience for (x, y) positions that must have equality & hash defined for consistency"""
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __repr__(self):
            return f'({self.x}, {self.y})'
    
        def __hash__(self):
            return hash((self.x, self.y))
    
        def __eq__(self, other):
            if isinstance(other, Position):
                return (self.x, self.y) == (other.x, other.y)
            return False
    
    # 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
    
    # Alternate element map...
    d = 5
    element_map = np.ones((d, d), dtype=int) - np.eye(d, dtype=int)
    print(element_map)
    
    matrix_a_rows = 3
    matrix_a_cols = 3
    matrix_a = np.zeros((matrix_a_rows, matrix_a_cols))
    
    
    def adj_xy(mat, p: Position):
        x, y = p.x, p.y
        res = []
        rows = len(mat) - 1
        cols = len(mat[0]) - 1
        for i in range(x - 1, x + 2):
            for j in range(y - 1, y + 2):
                if all((0 <= i <= rows, 0 <= j <= cols, (i, j) != (x, y))):
                    res.append(Position(i, j))
        return res
    
    
    # SET UP ILP
    m = pyo.ConcreteModel('matrix_fitter')
    
    # SETS
    
    m.E = pyo.Set(initialize=[Element(row) for row in element_map], doc='elements')
    m.P = pyo.Set(initialize=[Position(x, y) for x in range(len(matrix_a)) for y in range(len(matrix_a[0]))],
                  doc='positions')
    m.A = pyo.Set(initialize=list(range(len(element_map[0]))), doc='attribute')
    
    # VARS
    # place element e in position p based on attribute a being 0...
    m.place = pyo.Var(m.E, m.P, m.A, domain=pyo.Binary, doc='place')
    
    # OBJ:  Place as many as possible....  ideally --> len(matrix_a)
    m.obj = pyo.Objective(expr=pyo.sum_product(m.place), sense=pyo.maximize)
    
    
    # CONSTRAINTS
    @m.Constraint(m.E, m.P, m.A)
    def zero_attribute(m, e: Element, p, a):
        # can only place if attribute a is zero
        return m.place[e, p, a] * e.attribute(a) == 0
    
    
    @m.Constraint(m.P)
    def only_one(m, p):
        # can only place at most one element at each location
        return sum(m.place[e, p, a] for e in m.E for a in m.A) <= 1
    
    
    @m.Constraint(m.E, m.P, m.A)
    def adjacency(m, e: Element, p: Position, a):
        # neighbors must "add up" in the selected attribute (a), if placed in neighbor positions
        # we can "add up" the values of the selected attribute (a) for all adjacent positions that were selected
        neighbor_positions = adj_xy(matrix_a, p)
        return (sum(m.place[ee, pp, aa] * ee.attribute(a) for ee in m.E for pp in neighbor_positions for aa in m.A) >=
                len(neighbor_positions) * m.place[e, p, a])
    
    
    solver = pyo.SolverFactory('cbc')
    results = solver.solve(m, tee=True)
    print(results)
    if results.solver.termination_condition == pyo.TerminationCondition.optimal:
        for idx in m.place.index_set():
            if m.place[idx].value == 1:
                print(idx)
        if pyo.value(m.obj) == matrix_a_rows * matrix_a_cols:
            # all positions were filled
            print('success!')
        else:
            print(f'the max number of elements that can be placed is {pyo.value(m.obj)} / {matrix_a_rows * matrix_a_cols}')
    
    else:
        print('Problem with model...see results')
    

    Saída (truncada):

    ((0, 1, 1, 1, 1), (0, 1), 0)
    ((0, 1, 1, 1, 1), (2, 1), 0)
    ((1, 1, 0, 1, 1), (1, 0), 2)
    ((1, 1, 0, 1, 1), (1, 2), 2)
    ((1, 1, 1, 0, 1), (1, 1), 3)
    ((1, 1, 1, 1, 0), (0, 0), 4)
    ((1, 1, 1, 1, 0), (0, 2), 4)
    ((1, 1, 1, 1, 0), (2, 0), 4)
    ((1, 1, 1, 1, 0), (2, 2), 4)
    success!
    
    • 1

relate perguntas

  • Como divido o loop for em 3 quadros de dados individuais?

  • Como verificar se todas as colunas flutuantes em um Pandas DataFrame são aproximadamente iguais ou próximas

  • Como funciona o "load_dataset", já que não está detectando arquivos de exemplo?

  • Por que a comparação de string pandas.eval() retorna False

  • Python tkinter/ ttkboostrap dateentry não funciona quando no estado somente leitura

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    destaque o código em HTML usando <font color="#xxx">

    • 2 respostas
  • Marko Smith

    Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}?

    • 1 respostas
  • Marko Smith

    Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)?

    • 2 respostas
  • Marko Smith

    Por que as compreensões de lista criam uma função internamente?

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 respostas
  • Marko Smith

    Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)?

    • 4 respostas
  • Marko Smith

    Por que o construtor de uma variável global não é chamado em uma biblioteca?

    • 1 respostas
  • Marko Smith

    Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto?

    • 1 respostas
  • Marko Smith

    Somente operações bit a bit para std::byte em C++ 17?

    • 1 respostas
  • Martin Hope
    fbrereto Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi Por que as compreensões de lista criam uma função internamente? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A formato fmt %H:%M:%S sem decimais 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python std::views::filter do C++20 não filtrando a visualização corretamente 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa Por que o construtor de uma variável global não é chamado em uma biblioteca? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev Por que os compiladores perdem a vetorização aqui? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan Somente operações bit a bit para std::byte em C++ 17? 2023-08-17 17:13:58 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve