4x4行列の行列乗算に適した実行可能なアルゴリズムは何でしょうか。私はいくつかのアフィン変換を実装していますが、Strassenのように、効率的な行列乗算のためのアルゴリズムがいくつかあることを認識しています。しかし、小さい行列に対して特に効率的なアルゴリズムはありますか?私が一瞥したほとんどの情報源は、漸近的に最も効率的なものを調べています。
コメント
回答
Wikipediaには、 2つのnxn行列の行列乗算の4つのアルゴリズムがリストされています。
プログラマーが作成する古典的なアルゴリズムはO(n 3 )であり、「Schoolbook行列乗算」としてリストされています。うん。 O(n 3 )はちょっとしたヒットです。次善の策を見てみましょう。
Strassen algorithim はO(n 2.807 )です。これは機能します-いくつかの制限があり(サイズが2の累乗であるなど)、説明に注意事項があります:
従来の行列乗算と比較して、アルゴリズムは加算/減算にかなりのO(n 2 )ワークロードを追加します。したがって、特定のサイズ未満では、従来の乗算を使用することをお勧めします。
このアルゴリズムとその起源に関心のある方は、
Strassenはどのようにして行列の乗算方法を思いついたのですか?は良い読み物です。これは、追加される最初のO(n 2 )ワークロードの複雑さと、これが従来の乗算を実行するよりもコストがかかる理由を示しています。
つまり、実際にははO(n 2 + n 2.807 )であり、大きなOを書き出すときに低い指数 n に関するビットは無視されます。素晴らしい2048x2048マトリックスに取り組んでいるので、これは便利かもしれません。 4x4マトリックスの場合、オーバーヘッドが他のすべての時間を消費するため、おそらく遅くなるでしょう。
そして、 Coppersmith–Winogradがあります。アルゴリズムはO(n 2.373 )で、かなりの改善が加えられています。また、次の点にも注意が必要です。
Coppersmith–Winogradアルゴリズムは、理論的な時間境界を証明するために他のアルゴリズムの構成要素として頻繁に使用されます。ただし、Strassenアルゴリズムとは異なり、行列が非常に大きいために利点が得られないため、実際には使用されません。最新のハードウェアで処理されます。
したがって、超大規模なマトリックスで作業している場合は優れていますが、4x4のマトリックスでは役に立ちません。
これは、ウィキペディアの行列乗算:サブキュービックアルゴリズムのページに再び反映されています。これにより、処理速度が速くなる理由がわかります。
そのprのアルゴリズムが存在します単純なものよりも優れた実行時間を示します。最初に発見されたのは、1969年にVolker Strassenによって考案され、しばしば「高速行列乗算」と呼ばれるStrassenのアルゴリズムでした。これは、7回の乗算ではなく2つの2×2行列を乗算する方法に基づいています。通常の8)、いくつかの追加の加算および減算操作を犠牲にして、これを再帰的に適用すると、O(n log 2 7 )
O(n 2.807 )。Strassenのアルゴリズムはより複雑で、ナイーブアルゴリズムと比較して数値の安定性が低下しますが、次の場合は高速になります。 n> 100程度で、BLASなどのいくつかのライブラリに表示されます。
これが、アルゴリズムが高速である理由の核心になります。トレードオフです。いくつかの数値安定性といくつかの追加設定。 4x4マトリックスの追加セットアップは、より多くの乗算を行うコストよりもはるかに多くなります。
そして今、あなたの質問に答えるために:
しかし、小さい行列に対して特に効率的なアルゴリズムはありますか?
いいえ、O(n 3 )はかなり合理的に機能するため、4x4行列乗算用に最適化されたアルゴリズムはありません。オーバーヘッドのために大ヒットを喜んで受けられることに気付くまで。特定の状況では、行列について特定のことを事前に知っておくとオーバーヘッドが発生する可能性があります(一部のデータが再利用される量など)が、実際に最も簡単な方法は、O(n 3 )ソリューション、コンパイラに処理させ、後でプロファイルを作成して、実際に行列乗算のスロースポットであるコードがあるかどうかを確認します。
Math.SEに関連: 4x4行列を反転するために必要な最小の乗算
回答
多くの場合、非常に小さなセットでは単純なアルゴリズムが最速です。これは、より複雑なアルゴリズムは通常、オーバーヘッドを追加する変換を使用するためです。あなたの最善の策は、より効率的なアルゴリズム(ほとんどのライブラリは簡単な方法を使用すると思います)ではなく、より効率的な実装、たとえば、SIMD拡張命令(x86またはamd64コードを想定)を使用するか、アセンブリで手書きすることだと思います。また、メモリのレイアウトもよく考えておく必要があります。これについては十分なリソースを見つけることができるはずです。
回答
4x4マット/マット乗算の場合、アルゴリズムの改善が行われることがよくあります。 。基本的な3次時間計算量アルゴリズムは非常にうまくいく傾向があり、それよりも凝ったものは、時間を改善するよりも劣化する可能性が高くなります。ちょうど一般的に、拡張性の要素が含まれていない場合、派手なアルゴリズムは不適切です(例:単純な挿入やバブルソートではなく、常に 6つの要素を持つ配列をクイックソートしようとする)。参照の局所性を改善するためのここでの行列の転置のようなものも、行列全体が1つまたは2つのキャッシュラインに収まる場合、実際には参照の局所性を支援しません。この種のミニチュアスケールでは、4x4マット/マット乗算をまとめて実行している場合、改善は通常、適切なキャッシュラインの配置など、命令とメモリのマイクロレベルの最適化によってもたらされます。
コメント
- すばらしい答えです! ' SoAの頭字語を聞いたことがありません(少なくとも、オランダ語では' seksueel overdraagbare aandoening 'は'性感染症' …を意味しますが'うまくいけば、ここでの意味ではありません)。テクニックは明らかなようですが、私は'その名前があることに非常に驚いています。 SoAは何の略ですか?
- @Ruben Structures ofStructuresではなくStructureofArrays。 SoAsはPITAにすることもできます-最もクリティカルなパスのために保存するのが最適です。ここに'この件名で見つけた素敵な小さなリンクがあります: stackoverflow.com/questions/17924705/ …
- C ++ 11 / C11
alignas
。
回答
4x4を掛けるだけでよいことが確実にわかっている場合行列の場合、一般的なアルゴリズムについてまったく心配する必要はありません。2つのポインターを取り込んで、これを使用できます。
(これを自動化された方法で翻訳することを強くお勧めします)。
コンパイラは次のようになります。すべてを見ることができ、動的ループがなく、制御フローがないため、このコードを最適化するために(部分的な合計を再利用したり、数学を並べ替えたりするために)最適に配置されています。
これがなくても勝てるとは想像しがたいです。組み込み関数を使用します。
回答
n
を別の方法で定義すると、漸近的な複雑さを直接比較することはできません。リストなどのフラットなデータ構造でアルゴリズムの複雑さを比較することに慣れています。ここで、n
は合計の要素数として定義されています。リストですが、マトリックスアルゴリズムは、n
を1つの辺の長さのみとして定義します。
は、各要素を1回見て印刷するだけの簡単なもので、通常はO(n)と見なされますが、O(n 2 )です。 。 n
を行列内の要素の総数として定義する場合、つまり4x4行列の場合はn = 16の場合、ナイーブ行列の乗算はO(n 1.5 )、これはかなり良いです。
最善の策は、O(n 3 )が n
がフラットなデータ構造と同等に定義されている場合と同じくらい悪いです。
0 0 0 1
であるため、3Dのアフィン変換は4x3サブマトリックスのみを変更することに注意することで得られるパフォーマンスがあります。したがって、この行の乗算を回避できます。