由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}
表示。
transpose
的参数m
代表行大小,列大小由行大小和序列大小决定。该算法需要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 < (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,该过程继续进行,直到所有的循环都转换完毕。
这是可能的,但不平凡。像往常一样,维基百科有答案。 http://en.wikipedia.org/wiki/In-place_matrix_transposition – harold 2012-02-10 12:33:28
[前一段时间,我问了一个类似的问题,而不知道正确的术语](http://stackoverflow.com/q/3009379/237483) – 2012-02-10 12:49:17
感谢Harold!我将制定一个实施并在此处发布。 – UmNyobe 2012-02-10 12:49:54