矩阵的就地转置

时间:2012-02-10 12:26:06

标签: algorithm matrix

是否可以就地转置(m,n)矩阵,假设矩阵表示为大小为m*n的单个数组?

通常的算法

transpose(Matrix mat,int rows, int cols ){
    //construction step
    Matrix tmat;
    for(int i=0;i<rows;i++){
      for(int j=0;j<cols;j++){
       tmat[j][i] = mat[i][j];
      }
    }
 }
除非矩阵是方阵,否则

不适用于单个数组。 如果没有,那么所需的最小额外内存量是多少?

编辑:

我已经尝试了各种风格
for(int i=0;i<n;++i) {
  for(int j=0;j<i;++j) {
     var swap = m[i][j];
     m[i][j] = m[j][i];
     m[j][i] = swap;
  }
}

这是不正确的。在此特定示例中,m甚至不存在。在一条线上 矩阵mat[i][j] = mat[i*m + j],其中trans[j][i] = trans[i*n + j]

3 个答案:

答案 0 :(得分:16)

Wikipedia - Following the cycles算法描述的启发,我提出了以下C ++实现:

#include <iostream>  // std::cout
#include <iterator>  // std::ostream_iterator
#include <algorithm> // std::swap (until C++11)
#include <vector>

template<class RandomIterator>
void transpose(RandomIterator first, RandomIterator last, int m)
{
    const int mn1 = (last - first - 1);
    const int n   = (last - first) / m;
    std::vector<bool> visited(last - first);
    RandomIterator cycle = first;
    while (++cycle != last) {
        if (visited[cycle - first])
            continue;
        int a = cycle - first;
        do  {
            a = a == mn1 ? mn1 : (n * a) % mn1;
            std::swap(*(first + a), *cycle);
            visited[a] = true;
        } while ((first + a) != cycle);
    }
}

int main()
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
    transpose(a, a + 8, 4);
    std::copy(a, a + 8, std::ostream_iterator<int>(std::cout, " "));
}

该程序进行2×4矩阵的就地矩阵转置

0 1 2 3
4 5 6 7

row-major ordering {0, 1, 2, 3, 4, 5, 6, 7}中表示为4×2矩阵

0 4
1 5
2 6
3 7

由行主要排序{0, 4, 1, 5, 2, 6, 3, 7}表示。

m的参数transpose表示行大小,列大小n由行大小和序列大小决定。该算法需要m×n个辅助存储位来存储信息,这些信息已被交换。序列的索引使用以下方案映射:

0 → 0
1 → 2
2 → 4
3 → 6
4 → 1
5 → 3
6 → 5
7 → 7

映射功能一般是:

  

idx→(idx×n)mod(m×n-1),如果idx&lt; (m×n),idx→idx否则

我们可以确定此序列中的四个周期:{ 0 }{ 1, 2, 4 }{3, 5, 6}{ 7 }。每个循环可以独立于其他循环进行转置。变量cycle最初指向第二个元素(第一个元素不需要移动,因为0 → 0)。位数组visited保存已转置的元素,并指示需要移动索引1(第二个元素)。索引1与索引2(映射函数)交换。现在索引1保存索引2的元素,并且此元素与索引4的元素交换。现在索引1保存索引4的元素。索引4的元素应该转到索引1,它在正确的位置,转置循环结束后,所有触摸的索引都被标记为已访问。变量cycle递增,直到第一个未访问的索引为3.该过程继续此循环,直到所有循环都已转置。

答案 1 :(得分:2)

问题是,任务设置不正确。如果你的意思是“同一个地方”使用相同的矩阵,这是一个正确的任务。但是当你谈到写入内存中的同一区域时,“矩阵表示为大小为m * n的单个数组”,你必须添加如何在那里表示。除此之外,除了读取该矩阵的函数外,只需更改任何内容即可 - 只需交换其中的索引即可。

您希望将矩阵表示转置到内存中,以便索引的此矩阵的读取/设置功能保持不变。不是吗?

另外,我们不能记下不知道的算法,是按行或列写入内存的矩阵。好的,我们说它是按行写的。不是吗?

如果我们设置这两个缺乏条件,那么任务就变得正确并且不难解决。

简单地说,我们应该通过线性索引获取矩阵中的每个元素,找到它的行/列对,转置它,找到另一个结果线性索引并将值放入新的位置。问题是变换只是在方形矩阵的情况下才是自对称的,所以它实际上不能在现场完成。或者它可以,如果我们找到整个索引转换图并稍后在矩阵上使用它。

起始矩阵A:
    m-行数
    n列数
    nm - 元素数量
    li - 线性指数
    i - 列号
    j - 行号

得到矩阵B:
    lir - 得到的线性指数

转换数组转换

//preparation
for (li=0;li<nm;li++){
    j=li / n;
    i=li-j*n;
    lir=i*m+j;
    trans[li]=lir;
}

// transposition
for (li=0;li<nm;li++){
   cur=li;
   lir=trans[cur];
   temp2=a[lir];
   cur=lir;
   while (cur!=li){
      lir=trans[cur];
      temp1=a[cur];
      a[cur]=temp2;
      temp2=temp1;
      check[cur]=1;
      cur=lir;
   }
}

只有当细胞中存在重元素时,这种自动转置才有意义。

可以将trans []数组实现为函数。

答案 2 :(得分:0)

在一般情况下有效地执行此操作需要一些努力。非正方形和非对位算法不同。节省很多精力,只需使用FFTW即可。我之前就此事准备了a more complete write up,包括示例代码。