是否可以就地转置(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]
答案 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,包括示例代码。