変換階層を表すための効率的な構造


9

たとえば階層モデルで、行列のツリーを表すメモリ効率の良い方法を誰かが提案できますか?

特に、データの局所性を維持することに熱心であり、配列構造(行列&行列のインデックス)タイプのアプローチが適しているのではないかと思います。

多くの連鎖行列計算と同様に、この構造はおそらくメモリ内でかなりコピーされるため、連続したストレージを持つことは大きなボーナスになります。

回答:


6

アレイとしてのツリーは、私にとっては勝利のように聞こえます。階層の深さ優先トラバーサルを実行し、配列に入力するだけです。再帰を巻き戻すときに、子への絶対インデックスで親を更新するか、delta-from-meだけで更新することができ、子はどちらの方法でも親インデックスを保存できます。実際、相対オフセットを使用する場合は、ルートアドレスを持ち歩く必要はありません。構造はおそらく次のようになると思います

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

...したがって、(簡単に)可変サイズの構造を持つことができないので、兄弟に到達する方法を知るためのノードも必要です。変換オフセットの代わりにバイトオフセットを使用したと思いますが、変換ごとに子の数を可変にすることができます。

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
};

...その後、適切な場所に連続する変換を配置することを確認する必要があります。

ここに、子「ポインタ」が埋め込まれた完全に自己完結型のツリーを構築する方法を示します。

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);
}

(相対位置を保存する場合は- currentLocation、2つの「位置」書き込みに追加するだけです。)


C ++の場合は、子配列のサイズを指定するか、実行時にメモリを割り当てて作成する必要があります。
tenpn

公式のC99承認方法は、配列サイズの指定を空のままにすることです。

@ tenpn-アイデアは、目的に応じて構築されたバッファを使用することです。重要なのは、余分な割り当てを避けることです。配列のサイズがわからないため、配列サイズを指定することはできません。num個の子を書き込んだ後、子配列に書き込みますが、次の変換は子配列の終了後に開始されます。(これが、この構造にインデックスではなくバイトオフセットを使用する必要がある理由です。各エントリの大きさはわかりませんが、トラバースするのが効率的であり、1つの単位として移動できるように自己完結型です。)
ダッシュ-tom-bang

1
これは「構造体ハック」と呼ばれます。参照:informit.com/guides/content.aspx?g
Neverender

1
@tenpn別名可変長構造体。適切に使用すると、ヒープ割り当ての数を半分にすることができます。

1

行列の配列へのインデックス付けは、おそらく最も単純でメモリ効率の良いアプローチでしょう。

一連の変換は、マトリックス配列にインデックスを付ける一連のポインターまたは整数またはその他の小さな構造体としてLIFOに保持できます。これは、冗長な行列の格納を防ぐのに役立ち、データストレージコードをデータアクセサーコードから分離します。

最終的には、LIFOからインデックス値をプッシュしてポップし、変換チェーンを保存または再生します。

また、マトリックス構造に変換タイプ...回転、変換などを含めることができる場合は、メモリを少し節約することもできます。それ以外の場合は、タイプをインデックスとともに保存する必要があるため、重複が発生する可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.