Какой самый быстрый способ транспонирования матрицы в C++?

У меня есть матрица (относительно большая), которую мне нужно транспонировать. Например, предположим, что моя матрица имеет вид

a b c d e f
g h i j k l
m n o p q r 

Я хочу, чтобы результат был следующим:

a g m
b h n
c I o
d j p
e k q
f l r

Какой самый быстрый способ сделать это?

Комментарии к вопросу (13)
Решение

Это хороший вопрос. Есть много причин, по которым вы захотите действительно транспонировать матрицу в памяти, а не просто поменять координаты местами, например, при матричном умножении и гауссовом размазывании.

Сначала позвольте мне перечислить одну из функций, которые я использую для транспонирования (ПРИМЕЧАНИЕ: пожалуйста, смотрите конец моего ответа, где я нашел гораздо более быстрое решение)


void transpose(float *src, float *dst, const int N, const int M) {
    #pragma omp parallel for
    for(int n = 0; n
Комментарии (5)

Это зависит от вашего приложения, но в целом самым быстрым способом транспонирования матрицы будет инвертирование координат при поиске, тогда вам не придется перемещать данные.

Комментарии (7)

Некоторые подробности о переносе 4х4 квадратных поплавок (я буду обсуждать 32-разрядное целое число позже) матриц с x86-оборудовании. Это's полезн для начала здесь для того, чтобы перенести больше площади матриц 8х8 или 16х16.

_MM_TRANSPOSE4_PS(Р0, Р1, Р2, Р3) реализуется по-разному в разных компиляторах. На GCC и ICC (я не проверял лязг) unpcklps использовать, unpckhps, unpcklpd, unpckhpdтогда как для индекса MSVC использует только shufps. Мы можем объединить эти два подхода вместе.

t0 = _mm_unpacklo_ps(r0, r1);
t1 = _mm_unpackhi_ps(r0, r1);
t2 = _mm_unpacklo_ps(r2, r3);
t3 = _mm_unpackhi_ps(r2, r3);

r0 = _mm_shuffle_ps(t0,t2, 0x44);
r1 = _mm_shuffle_ps(t0,t2, 0xEE);
r2 = _mm_shuffle_ps(t1,t3, 0x44);
r3 = _mm_shuffle_ps(t1,t3, 0xEE);

Одно интересное наблюдение заключается в том, что два тасует может быть преобразован к одному перемешать и две бленды (SSE4.1) как это.

t0 = _mm_unpacklo_ps(r0, r1);
t1 = _mm_unpackhi_ps(r0, r1);
t2 = _mm_unpacklo_ps(r2, r3);
t3 = _mm_unpackhi_ps(r2, r3);

v  = _mm_shuffle_ps(t0,t2, 0x4E);
r0 = _mm_blend_ps(t0,v, 0xC);
r1 = _mm_blend_ps(t2,v, 0x3);
v  = _mm_shuffle_ps(t1,t3, 0x4E);
r2 = _mm_blend_ps(t1,v, 0xC);
r3 = _mm_blend_ps(t3,v, 0x3);

Это эффективное преобразование 4 тасует в 2 тасует и 4 смеси. При этом используется более 2 инструкций, чем реализация ССЗ, МУС, и MSVC. Преимущество в том, что он уменьшает давление, которое может иметь преимущества в некоторых случаях. В настоящее время все перемешивает и распаковывает можете пойти только на один конкретный порт, а смеси может перейти к любому из двух разных портов.

Я попытался с помощью 8 тасует, как MSVC и преобразования в 4 тасует + 8 блендов, но он не работал. Я все еще должен был использовать 4 распаковывает.

Я использовал этот же метод для 8х8 плавающий транспонировать (см. В конце ответа). https://stackoverflow.com/a/25627536/2542702. В том, что ответа я все равно пришлось использовать 8 распаковывает но я manged, чтобы преобразовать 8 тасует в 4 тасует и 8 блендов.

Для 32-разрядных целых чисел нет ничего похожего на shufps (за исключением 128-битное тасует с AVX512), поэтому она может быть осуществлена только с распаковывает, которые я не'т думаю, что можно преобразовать в смеси (эффективно). Эффективно действует AVX512vshufi32x4 " как " shufps, кроме 128-битное полосы из 4 чисел вместо 32-разрядных плавает, так этот же метод может быть возможно с vshufi32x4 в некоторых случаях. С рыцарями посадки тасует в четыре раза медленнее (пропускная способность), чем смеси.

Комментарии (6)

транспонирование без каких-либо накладных расходов (класс не полный):

class Matrix{
   double *data; //suppose this will point to data
   double _get1(int i, int j){return data[i*M+j];} //used to access normally
   double _get2(int i, int j){return data[j*N+i];} //used when transposed

   public:
   int M, N; //dimensions
   double (*get_p)(int, int); //functor to access elements  
   Matrix(int _M,int _N):M(_M), N(_N){
     //allocate data
     get_p=&Matrix::_get1; // initialised with normal access 
     }

   double get(int i, int j){
     //there should be a way to directly use get_p to call. but i think even this
     //doesnt incur overhead because it is inline and the compiler should be intelligent
     //enough to remove the extra call
     return (this->*get_p)(i,j);
    }
   void transpose(){ //twice transpose gives the original
     if(get_p==&Matrix::get1) get_p=&Matrix::_get2;
     else get_p==&Matrix::_get1; 
     swap(M,N);
     }
}

можно использовать такой:

Matrix M(100,200);
double x=M.get(17,45);
M.transpose();
x=M.get(17,45); // = original M(45,17)

конечно, я этого'т возиться с управление памяти вот, что имеет решающее значение, но разные темы.

Комментарии (1)
template 
void transpose( std::vector< std::vector > a,
std::vector< std::vector > b,
int width, int height)
{
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            b[j][i] = a[i][j];
        }
    }
} 
Комментарии (11)

Рассмотрим каждую строку в качестве столбцов, а каждый столбец как строку .. используйте j,i вместо I и J

демо: http://ideone.com/lvsxKZ


#include  
using namespace std;

int main ()
{
    char A [3][3] =
    {
        { 'a', 'b', 'c' },
        { 'd', 'e', 'f' },
        { 'g', 'h', 'i' }
    };

    cout 
Комментарии (0)

Интел мкл указывает на место и место переноса/копирования матриц. вот ссылка на документацию. Я бы рекомендовал пробовать из места внедрения как быстрее десяти на месте и в документации последняя версия мкл содержит некоторые ошибки.

Комментарии (0)

Если размер массива известен до того, как мы могли бы использовать союз с нашей помощью. Как это-

``

включать <бит/stdc++.ч>

с помощью пространства имен std;

Союз уа{ инт модуль arr[2][3]; инт брр[3][2]; };

тап_п() { Союз уа БПЛА; инт Карр[2][3] = {{1,2,3},{4,5,6}}; функции memcpy(БПЛА.Арр Карр как sizeof(Карр)); для (int я=0;я&Л;3;я++) { для (Int J=0 и;ж<2;к++) соиь<<БПЛА.брр[я][Дж]<<" по себе "; соиь<<'\П'; }

возврат 0; } ``

Комментарии (0)

Современные библиотеки линейной алгебры включают в себя оптимизированные версии из самых распространенных операций. Многие из них включают динамическая диспетчеризация процессора, который выбирает лучшее внедрение оборудования во время выполнения программы (без ущерба для портативности).

Обычно это лучшая альтернатива ручной оптимизации вашего functinos через вектор расширения встроенных функций. Последний будет связывать реализацию конкретного производителя оборудования и модели: если вы решите поменять поставщика (например, руку) или до новой векторных расширений (например, AVX512), вам придется повторно реализовать его снова, чтобы получить большинство из них.

МКЛ транспозиции, например, включает в себя imatcopy функция расширения Блас. Вы можете найти его в других реализациях, таких как OpenBLAS, а также:

включать <мкл.ч>

пустота транспонировать( поплавок* а, инт Н, инт м ) { константный тип char row_major = 'Р'; константный тип char транспонирует = 'Т'; константный поплавок Альфа = 1.0 Ф; mkl_simatcopy (row_major, транспонировать, Н, М, АЛЬФА, а, н, н); } ``

Для проекта c++, вы можете воспользоваться броненосца на C++: ``

включать <броненосца>

пустота транспонировать( АРМА::мат &матрица ) { АРМА::inplace_trans(матрицы); } ``

Комментарии (0)

Я думаю, что самый быстрый способ не следует принимать выше, чем О(П^2) аналогичным способом можно использовать только O(1) по пространству : лучший способ сделать это, чтобы поменять в паре, потому что, когда вы транспонировать матрицу, то что вы делаете это: м[я][Дж]=м[Дж][я] , так что магазин, М[я][Дж] в темп, то m[я][Дж]=м[Дж][я],и последний шаг : м[Дж][я]=темп. это может быть сделано за один проход, так что его следует принимать за О(N^2)

Комментарии (1)

мой ответ является транспонированной матрицы 3х3


 #include

#include

main()
{
int a[3][3];
int b[3];
cout
Комментарии (0)