Estrutura eficiente para representar uma hierarquia de transformação

9

Alguém pode sugerir uma maneira eficiente de memória para representar uma árvore de matrizes, por exemplo, em um modelo de hierarquia?

Estou particularmente interessado em preservar a localidade dos dados e suspeito que uma abordagem do tipo estrutura de matrizes (matrizes e índices para matrizes) possa ser adequada.

Assim como muitos cálculos matriciais encadeados, essa estrutura provavelmente será copiada na memória um pouco, de modo que ter armazenamento contíguo seria um grande bônus.

Justicle
fonte

Respostas:

6

A árvore como matriz parece uma vitória para mim. Basta fazer uma travessia profunda da sua hierarquia e preencher uma matriz; ao retroceder a recursão, você pode atualizar o pai com o índice absoluto para o filho ou apenas o delta-de-mim, e os filhos também podem armazenar os índices pai de qualquer maneira. De fato, se você usar deslocamentos relativos, não precisará carregar o endereço raiz. Suponho que a estrutura provavelmente seria algo como

struct Transform
{
   Matrix m; // whatever you like
   int parent;   // index or offset, you choose!
   int sibling;
   int firstchild;
};

... então você precisará de nós para saber como chegar aos irmãos, já que você não pode (facilmente) ter uma estrutura de tamanho variável. Embora eu ache que, se você usou desvios de bytes em vez de desvios de transformação, poderá ter um número variável de filhos por transformação:

struct Transform
{
   Matrix m; // whatever you like
   int parent;  // negative byte offest
   int numchildren;
   int child[0]; // can't remember if you put a 0 there or leave it empty;
                 // but it's an array of positive byte offsets
};

... então você só precisa ter certeza de colocar as transformações sucessivas no lugar certo.

Veja como você constrói uma árvore totalmente independente com "ponteiros" filhos incorporados.

int BuildTransforms(Entity* e, OutputStream& os, int parentLocation)
{
    int currentLocation = os.Tell();

    os.Write(e->localMatrix);
    os.Write(parentLocation);
    int numChildren = e->GetNumChildren();
    os.Write(numChildren);

    int childArray = os.Tell();
    os.Skip(numChildren * sizeof(int));
    os.AlignAsNecessary();  // if you need to align transforms

    childLocation = os.Tell();
    for (int i = 0; i < numChildren; ++i) {
        os.Seek(childArray + (i * sizeof(int)));
        os.Write(childLocation);
        os.Seek(childLocation);
        childLocation = BuildTransforms(e->GetChild(i), os, currentLocation);
    }

    return os.Tell();
}

void BuildTransforms(Entity* root)
{
    OutputStream os;
    BuildTransforms(root, os, -1, 0);
}

(Se você deseja armazenar locais relativos, basta adicionar - currentLocationàs duas gravações "locais".)

traço-tom-bang
fonte
Se estivermos falando de C ++, será necessário especificar um tamanho para sua matriz filha ou criá-la em tempo de execução com uma alocação de memória.
tenpn
A maneira oficial aprovada pela C99 é deixar em branco a especificação do tamanho da matriz.
@ tenpn- a idéia é que você tenha um buffer construído de propósito. O objetivo é evitar alocações extras; você não pode especificar o tamanho da matriz porque não sabe qual será o tamanho. Depois de escrever num children, você grava na matriz filho, mas a próxima transformação é iniciada após o término da matriz filho. (É por isso que você precisa usar desvios de bytes e não índices para essa estrutura; você não sabe o tamanho de cada entrada, mas ainda é eficiente para percorrer e é independente para poder se mover como uma unidade.)
dash -tom-bang
11
Isso é chamado de "truque de estrutura". Consulte também: informit.com/guides/content.aspx?g=cplusplus&seqNum=288
Neverender
11
@tenpn Aka estruturas de comprimento variável. Utilizados adequadamente, eles podem reduzir pela metade o número de alocações de heap.
1

A indexação em matriz de matrizes provavelmente seria a abordagem mais direta e eficiente em termos de memória.

Uma cadeia de transformações pode ser mantida em um LIFO como uma série de ponteiros ou números inteiros ou outra estrutura pequena que indexa na matriz. Isso ajudaria a impedir o armazenamento de matrizes redundantes e separaria o código de armazenamento de dados do código do acessador de dados.

Por fim, basta pressionar e exibir valores de índice do LIFO para armazenar ou reproduzir sua cadeia de transformação.

Você também poderá economizar um pouco de memória se a estrutura da matriz também puder conter o tipo de transformação ... rotação, conversão, etc. Caso contrário, o tipo precisará ser armazenado com o índice, resultando em maior duplicação possível.

Oxidado
fonte