C 转置二维数组

C 转置二维数组,c,performance,algorithm,embedded,matrix,C,Performance,Algorithm,Embedded,Matrix,如何有效地转置矩阵?是否有用于此的库,或者您将使用什么算法 例如: short src[W*H]={ {1,2,3}, {4,5,6} }; 短目的地[W*H]; 顺时针旋转90°(目的地、src、W、H)// 在O(1)中,一个非常简单的解决方案是为矩阵保存一个额外的布尔值,说明它是否是“转置”的。 然后根据该布尔值(行/列或列/行)访问数组 当然,这会妨碍缓存的利用率 因此,如果您有许多转置操作,并且很少有“完全遍历”(顺便说一句,也可能根据布尔值重新排序),这是您的最佳选择 如果矩阵是正

如何有效地转置矩阵?是否有用于此的库,或者您将使用什么算法

例如:

short src[W*H]={
{1,2,3},
{4,5,6}
};
短目的地[W*H];

顺时针旋转90°(目的地、src、W、H)// 在O(1)中,一个非常简单的解决方案是为矩阵保存一个额外的布尔值,说明它是否是“转置”的。 然后根据该布尔值(行/列或列/行)访问数组

当然,这会妨碍缓存的利用率

因此,如果您有许多转置操作,并且很少有“完全遍历”(顺便说一句,也可能根据布尔值重新排序),这是您的最佳选择

  • 如果矩阵是正方形的,或者如果您不想在原地转置,那么很容易:
基本上,您在行上迭代,并用匹配的列项交换每个项。您可以通过交换行和列索引来获得匹配项。处理完所有列后,换位完成。您也可以反过来迭代列

如果要提高性能,可以将完整的行复制到临时数组中,将完整的匹配列复制到另一个数组中,然后将它们复制回来。如果将memcopy用于涉及最内层元素的传输,则速度应该会稍快一些(即使此策略涉及一个或多个变量赋值)

  • 如果矩阵不是正方形(如您的示例中所示),在适当的位置执行它确实很棘手。因为转置不会改变内存需求,所以看起来仍然可以在原地执行,但如果不小心执行,最终会覆盖另一行或列的元素
如果内存不是瓶颈,我建议使用临时矩阵。这真的很容易,而且无论如何可能会更快

  • 最好的方法是根本不转置,而只是在某个地方设置一个标志,说明是先访问数据行还是先访问数据列。在大多数情况下,需要换位的算法可以重写以访问未换位的矩阵。要实现这一点,您只需重写一些基本操作,如矩阵积,以接受具有一个方向或另一个方向的矩阵
但在某些情况下,我理解这是不可能的,通常情况下,如果数据准备由某些现有硬件或库访问。

维基百科有一个就地矩阵转换。对于非平方矩阵,这是一个非常重要、相当有趣的问题(即使用少于O(nxm)的内存)。这篇文章有很多关于算法的文章的链接,还有一些源代码

但是要注意——正如我在对你的问题的评论中所说的,你的演示不是标准的换位,所有的算法都是为这个换位而编写的

(标准换位函数将为示例数据提供此结果:)


如果您这样做只是为了在屏幕上显示图像,那么最好在将图像复制到后缓冲区时进行转置,而不是原地转置,然后进行闪烁。

只需简单地复制到临时文件,然后再进行复制,边走边转置,使用指针步进避免地址计算中的乘法,内部循环展开:

char temp[W*H];
char* ptemp = temp;
memcpy(temp, array, sizeof(char)*W*H);
for (i = 0; i < H; i++){
    char* parray = &array[i];
    for (j = 0; j+8 <= W; j += 8, ptemp += 8){
        *parray = ptemp[0]; parray += H;
        *parray = ptemp[1]; parray += H;
        *parray = ptemp[2]; parray += H;
        *parray = ptemp[3]; parray += H;
        *parray = ptemp[4]; parray += H;
        *parray = ptemp[5]; parray += H;
        *parray = ptemp[6]; parray += H;
        *parray = ptemp[7]; parray += H;
    }
    for (; j < W; j++, parray += H){
        *parray = *ptemp++;
    }
}
chartemp[W*H];
char*ptemp=温度;
memcpy(温度、阵列、大小(字符)*W*H);
对于(i=0;i对于(j=0;j+8,在某些情况下,有相应的库。值得注意的是,可以使用矢量化数据(例如,128位向量中的四个32位元素,但这也适用于32位寄存器中的四个8位字节)比单个元素访问速度更快

对于转置,标准想法是使用“shuffle”指令,该指令允许您以任意顺序从两个现有向量中创建新的数据向量。您使用输入数组的4x4块。因此,首先,您有:

v0 = 1 2 3 4
v1 = 5 6 7 8
v2 = 9 A B C
v3 = D E F 0
然后,对前两个向量(交错奇数元素,A0B0 C0D0->ABCD,交错偶数元素,0A0B 0C0D->ABCD)和后两个向量应用洗牌指令,以创建每个2x2块转置的新向量集:

1 5 3 7
2 6 4 8
9 D B F
A E C 0
最后,对奇数对和偶数对应用洗牌指令(结合它们的第一对元素AB00 CD00->ABCD和最后一对元素00AB 00CD->ABCD),以获得:

在那里,16个元素被转换成8个指令

现在,对于32位寄存器中的8位字节,ARM并没有确切的洗牌指令,但您可以通过移位和SEL(select)指令合成所需的内容,第二组洗牌可以在一条指令中使用PKHBT(pack-halfword-bottom-top)和PKHTB(pack-halfword-top-bottom)指令完成


最后,如果您使用的是带有霓虹灯矢量化的大型ARM处理器,您可以使用16x16块上的16个元素向量来执行类似操作。

这里最有效的解决方案是在数据从RAM复制到帧缓冲区时旋转数据。在RAM中旋转源,然后将结果复制到帧缓冲区,这将是最好的,是复制和旋转版本速度的一半。因此,问题是,顺序读取和随机写入还是随机读取和顺序写入更有效。在代码中,这将是以下两种选择之一:

// read sequential
src = { image data }
dest = framebuffer
for (y = 0 ; y < H ; ++y)
{
   for (x = 0 ; x < W ; ++x)
   {
     pixel = *src++
     dest [y,x] = pixel
   }
}
//按顺序读取
src={image data}
dest=帧缓冲区
对于(y=0;y
或:

//按顺序写入
src={image data}
dest=帧缓冲区
对于(x=0;x
对此的答案只能通过分析代码来确定

现在,它可能是你有一个GPU,在这种情况下,它肯定有能力做旋转,这将是更有效的让t
1 5 3 7
2 6 4 8
9 D B F
A E C 0
1 5 9 D
2 6 A E
3 7 B F
4 8 C 0
// read sequential
src = { image data }
dest = framebuffer
for (y = 0 ; y < H ; ++y)
{
   for (x = 0 ; x < W ; ++x)
   {
     pixel = *src++
     dest [y,x] = pixel
   }
}
// write sequential
src = { image data }
dest = framebuffer
for (x = 0 ; x < W ; ++x)
{
   for (y = 0 ; y < H ; ++y)
   {
     pixel = src [x,y]
     *dest++ = pixel
   }
}