web-dev-qa-db-ja.com

バッファリングなしで行列を転置する

「バッファリング」行列を使用せずにMxN行列を転置するにはどうすればよいですか?後者の場合、それは簡単で、かなり「古典的な」アプローチです...

_matrix[M][N]_が与えられた場合、これをvoid transpose_matrix(int matrix[][])関数に入れて、参照として渡されたマトリックスを直接操作します。これは、以前に言ったように、マトリックスのメモリ空間を編集してその要素を転置します。

例えば:

A =

_1 2 3 4
5 6 7 8
9 1 2 3
_

A自己転置後(きれいな印刷なし)[1]:

_1 5 9 2
6 1 3 7
2 4 8 3
_

AはNxMマトリックスでどのように見えますか:

_1 5 9
2 6 1
3 7 2
4 8 3
_

ありがとう!

[1]:実際に「新しい」NxMマトリックスであるかのようにマトリックスを出力します。

5
peperunas

それを効率的に行うことは複雑です。

ウィキペディアには、この件に関する素晴らしい記事があります: インプレースマトリックス転置 には、多くの興味深い参照があります。

O(1)補助記憶域の要件(および非常に非連続的なメモリアクセス))を使用した単純な "フォローザサイクル" Cの実装は次のとおりです。

_/// \param[in] m input matrix
/// \param[in] h number of rows of \a m
/// \param[in] w number of columns of \a m
///
/// Performs in-place transposition of matrix \a m.
void transpose(double m[], const unsigned h, const unsigned w)
{
  for (unsigned start = 0; start <= w * h - 1; ++start)
  {
    unsigned next = start;
    unsigned i = 0;
    do
    {
      ++i;
      next = (next % h) * w + next / h;
    } while (next > start);

    if (next >= start && i != 1)
    {
      const double tmp = m[start];
      next = start;
      do
      {
        i = (next % h) * w + next / h;
        m[next] = (i == start) ? tmp : m[i];
        next = i;
      } while (next > start);
    }
  }
}
_

O(MN)補助記憶装置、要素ごとに1ビット、 hereIn-place transposition of行列 Stackoverflowの質問) 正方形を表さない1次元配列の転置 も見てください。

[〜#〜] ps [〜#〜]多くの状況では、行列を転置された順序に物理的に並べ替える必要がないか、または望ましくありません。 :特定の行列を転置順に解釈することを指定するオプションを提供するだけで十分な場合があります。


上記のアルゴリズムの詳細(質問の入力行列A(3;4)を想定):

最初のチャンク:(dowhile)は一種のシーケンスジェネレーターです:

_next-sequence  start     next    i
-----------------------------------
[0]                0        0    1
[1,4,5,9,3]        1        1    5       <-
[2,8,10,7,6]       2        2    5       <-
[3,1]              3        1    1
[4,5,9,3]          4        3    3
[5,9,3]            5        3    2
[6,2]              6        2    1
[7,6]              7        6    1
[8,10,7]           8        7    2
[9,3]              9        3    1
[10,7]            10        7    1
[11]              11       11    1
_

すべてのシーケンスが役立つわけではありません。

  • 長さ1のシーケンス(_i == 1_、たとえば_[0]_および_[11]_)は、データの移動がないことを意味し、スキップできます。
  • nextの最終値がstartよりも小さいシーケンスは、すでに見られたシーケンスのサブシーケンスであるため、スキップできます。

2番目のチャンク(if)は、シーケンスで記述されたデータモーションを実行します。

例えば。 _[1,4,5,9,3]_は、位置4の値が位置1に移動し、位置5の値が位置4に移動することを意味します。一方、位置1は位置3に移動します(サイクル/順列の標準表記を使用して記述された_(1 3 9 5 4)_)。

式について:

_(position % h) * w + position / h;
_

これは、元の行列と転置行列の間の位置を変換する方法です。

8
manlio