Каков самый быстрый способ транспонировать матрицу в 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

Как это сделать быстрее всего?


person mans    schedule 24.05.2013    source источник
comment
Это называется транспонированием. Совершенно другое понятие - поворот на 90 градусов.   -  person Andy Prowl    schedule 24.05.2013
comment
Кроме того, это ведь не 90 градусов? Если бы это были первые две строки, это были бы m g a и n h b.   -  person Some programmer dude    schedule 24.05.2013
comment
И самый быстрый способ - не повернуть его, а просто поменять местами порядок индекса при доступе к массиву.   -  person High Performance Mark    schedule 24.05.2013
comment
Если встроенные макросы Intel считаются как C, это будет _MM_TRANSPOSE(). :-)   -  person Damon    schedule 24.05.2013
comment
Независимо от того, насколько это быстро, вы все равно должны получить доступ ко всем элементам матрицы.   -  person taocp    schedule 24.05.2013
comment
@HighPerformanceMark: Я предполагаю, что это зависит от того, если вы затем захотите повторно обращаться к матрице в порядке строк, наличие транспонированного флага сильно ударит по вам.   -  person Matthieu M.    schedule 24.05.2013
comment
Если ваша матрица может быть представлена ​​в линейной памяти (одномерный массив) и в строках ‹› столбцов (т.е. не в квадрате), то этот ответ может быть полезен: stackoverflow.com/a/3514733/192510   -  person NealB    schedule 24.05.2013
comment
@HighPerformanceMark, если матрица хранится как 2D-массив, замена индексов не будет работать, если количество столбцов и строк не равно. В конечном итоге вы получите доступ к памяти за пределами массива!   -  person Marc Claesen    schedule 25.05.2013
comment
Транспонирование матриц печально известно проблемами, связанными с кешами памяти. Если ваш массив достаточно велик, чтобы производительность транспонирования была значительной, и вы не можете избежать транспонирования, просто предоставив интерфейс с замененными индексами, то лучшим вариантом является использование существующей библиотечной процедуры для транспонирования больших матриц. Эту работу специалисты уже проделали, и вам стоит ею воспользоваться.   -  person Eric Postpischil    schedule 25.05.2013
comment
Полезная информация содержится в этот вопрос. (Среди прочего: увеличение размера матрицы может ускорить транспонирование.)   -  person Eric Postpischil    schedule 25.05.2013
comment
Оказывается, мозаика / блокировка петель также помогает при транспонировании. stackoverflow.com/questions/5200338/   -  person    schedule 25.05.2013
comment
Итак, я изучил это и обновил свой ответ. Я нашел решение, которое намного быстрее, чем то, что я использовал, используя блокировку цикла.   -  person    schedule 27.05.2013
comment
Я снова нашел более быстрое решение с использованием SSE, блокировки циклов и OpenMP. Я обновил свой ответ.   -  person    schedule 29.05.2013


Ответы (11)


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

Сначала позвольте мне перечислить одну из функций, которые я использую для транспонирования (РЕДАКТИРОВАТЬ: пожалуйста, посмотрите конец моего ответа, где я нашел гораздо более быстрое решение)

void transpose(float *src, float *dst, const int N, const int M) {
    #pragma omp parallel for
    for(int n = 0; n<N*M; n++) {
        int i = n/N;
        int j = n%N;
        dst[n] = src[M*j + i];
    }
}

Теперь посмотрим, почему транспонирование полезно. Рассмотрим умножение матриц C = A * B. Мы могли бы сделать это так.

for(int i=0; i<N; i++) {
    for(int j=0; j<K; j++) {
        float tmp = 0;
        for(int l=0; l<M; l++) {
            tmp += A[M*i+l]*B[K*l+j];
        }
        C[K*i + j] = tmp;
    }
}

Таким образом, однако, будет много промахов в кеше. Гораздо более быстрое решение - сначала перенести букву B.

transpose(B);
for(int i=0; i<N; i++) {
    for(int j=0; j<K; j++) {
        float tmp = 0;
        for(int l=0; l<M; l++) {
            tmp += A[M*i+l]*B[K*j+l];
        }
        C[K*i + j] = tmp;
    }
}
transpose(B);

Умножение матрицы - O (n ^ 3), а транспонирование - O (n ^ 2), поэтому транспонирование должно иметь незначительное влияние на время вычислений (для больших n). В матричном умножении замощение петли даже более эффективно, чем транспонирование, но это намного сложнее.

Хотел бы я знать более быстрый способ выполнить транспонирование (Изменить: я нашел более быстрое решение, см. Конец моего ответа). Когда через несколько недель выйдет Haswell / AVX2, у него будет функция сбора данных. Не знаю, поможет ли это в данном случае, но я мог бы представить, как собираю столбец и записываю строку. Возможно, это сделает транспонирование ненужным.

Для смазывания по Гауссу вы делаете мазки по горизонтали, а затем по вертикали. Но размазывание по вертикали вызывает проблемы с кешем, поэтому вам нужно

Smear image horizontally
transpose output 
Smear output horizontally
transpose output

Вот документ Intel, в котором объясняется, что http://software.intel.com/en-us/articles/iir-gaussian-blur-filter-implementation-using-intel-advanced-vector-extensions

Наконец, то, что я на самом деле делаю при умножении матриц (и при размытии по Гауссу), - это не точное транспонирование, а транспонирование по ширине определенного размера вектора (например, 4 или 8 для SSE / AVX). Вот функция, которую я использую

void reorder_matrix(const float* A, float* B, const int N, const int M, const int vec_size) {
    #pragma omp parallel for
    for(int n=0; n<M*N; n++) {
        int k = vec_size*(n/N/vec_size);
        int i = (n/vec_size)%N;
        int j = n%vec_size;
        B[n] = A[M*i + k + j];
    }
}

РЕДАКТИРОВАТЬ:

Я попробовал несколько функций, чтобы найти наиболее быстрое транспонирование для больших матриц. В конце концов, самым быстрым результатом является использование блокировки цикла с block_size=16 (Изменить: я нашел более быстрое решение с использованием SSE и блокировки цикла - см. Ниже). Этот код работает для любой матрицы NxM (т.е. матрица не обязательно должна быть квадратной).

inline void transpose_scalar_block(float *A, float *B, const int lda, const int ldb, const int block_size) {
    #pragma omp parallel for
    for(int i=0; i<block_size; i++) {
        for(int j=0; j<block_size; j++) {
            B[j*ldb + i] = A[i*lda +j];
        }
    }
}

inline void transpose_block(float *A, float *B, const int n, const int m, const int lda, const int ldb, const int block_size) {
    #pragma omp parallel for
    for(int i=0; i<n; i+=block_size) {
        for(int j=0; j<m; j+=block_size) {
            transpose_scalar_block(&A[i*lda +j], &B[j*ldb + i], lda, ldb, block_size);
        }
    }
}

Значения lda и ldb - это ширина матрицы. Они должны быть кратны размеру блока. Чтобы найти значения и выделить память, например, матрица 3000х1001 делаю примерно так

#define ROUND_UP(x, s) (((x)+((s)-1)) & -(s))
const int n = 3000;
const int m = 1001;
int lda = ROUND_UP(m, 16);
int ldb = ROUND_UP(n, 16);

float *A = (float*)_mm_malloc(sizeof(float)*lda*ldb, 64);
float *B = (float*)_mm_malloc(sizeof(float)*lda*ldb, 64);

Для 3000x1001 это возвращает ldb = 3008 и lda = 1008

Изменить:

Я нашел еще более быстрое решение с использованием встроенных функций SSE:

inline void transpose4x4_SSE(float *A, float *B, const int lda, const int ldb) {
    __m128 row1 = _mm_load_ps(&A[0*lda]);
    __m128 row2 = _mm_load_ps(&A[1*lda]);
    __m128 row3 = _mm_load_ps(&A[2*lda]);
    __m128 row4 = _mm_load_ps(&A[3*lda]);
     _MM_TRANSPOSE4_PS(row1, row2, row3, row4);
     _mm_store_ps(&B[0*ldb], row1);
     _mm_store_ps(&B[1*ldb], row2);
     _mm_store_ps(&B[2*ldb], row3);
     _mm_store_ps(&B[3*ldb], row4);
}

inline void transpose_block_SSE4x4(float *A, float *B, const int n, const int m, const int lda, const int ldb ,const int block_size) {
    #pragma omp parallel for
    for(int i=0; i<n; i+=block_size) {
        for(int j=0; j<m; j+=block_size) {
            int max_i2 = i+block_size < n ? i + block_size : n;
            int max_j2 = j+block_size < m ? j + block_size : m;
            for(int i2=i; i2<max_i2; i2+=4) {
                for(int j2=j; j2<max_j2; j2+=4) {
                    transpose4x4_SSE(&A[i2*lda +j2], &B[j2*ldb + i2], lda, ldb);
                }
            }
        }
    }
}
person Community    schedule 24.05.2013
comment
Хороший снимок, но я не уверен, что «Умножение матриц - это O (n ^ 3)», я думаю, что это O (n ^ 2). - person ulyssis2; 04.12.2016
comment
@ ulyssis2 Это O (n ^ 3), если вы не используете умножение матриц Штрассена (O (n ^ 2.8074)). user2088790: Очень хорошо сделано. Храню это в моей личной коллекции. :) - person saurabheights; 28.12.2016
comment
В случае, если кто-то хочет знать, кто написал этот ответ, это был я. Я ушел из SO однажды, преодолел это и вернулся. - person Z boson; 16.03.2017
comment
@ ulyssis2 Наивное умножение матриц определенно равно O (n ^ 3), и, насколько я знаю, вычислительные ядра реализуют наивный алгоритм (я думаю, это потому, что Штрассен в конечном итоге выполняет гораздо больше операций (сложений), что плохо, если можно делать быстрые продукты, но я могу ошибаться). Это открытый вопрос, может ли умножение матриц быть O (n ^ 2) или нет. - person étale-cohomology; 09.05.2017
comment
Обычно лучше полагаться на библиотеку линейной алгебры, которая сделает всю работу за вас. Современные библиотеки, такие как Intel MKL, OpenBLAS и т. Д., Обеспечивают динамическую диспетчеризацию ЦП, которая выбирает лучшую реализацию, доступную для вашего оборудования (например, могут быть доступны более широкие векторные регистры, чем SSE: AVX AVX2, AVX512 ...), поэтому вы не должны Нет необходимости делать непереносимую программу, чтобы получить быструю программу. - person Jorge Bellon; 21.09.2019
comment
Обратите внимание, что последний фрагмент SSE не будет работать правильно, если количество строк и количество столбцов не кратно 4. Он оставит нетронутыми граничные ячейки. - person Sopel; 27.10.2020

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

person Shafik Yaghmour    schedule 24.05.2013
comment
Под инвертированием координат вы имеете в виду переключение осей x и y? - person taocp; 24.05.2013
comment
Это замечательно, если это небольшая матрица или вы читаете ее только один раз. Однако, если транспонированная матрица велика и ее нужно использовать много раз, вы все равно можете сохранить быструю транспонированную версию, чтобы получить лучший шаблон доступа к памяти. (+1, кстати) - person Agentlien; 24.05.2013
comment
@Agentlien: Почему A [j] [i] будет медленнее, чем A [i] [j]? - person beaker; 24.05.2013
comment
@beaker Если у вас большая матрица, разные строки / столбцы могут занимать разные строки / страницы кеша. В этом случае вы захотите перебрать элементы таким образом, чтобы получить доступ к соседним элементам друг за другом. В противном случае это может привести к тому, что доступ к каждому элементу станет промахом в кэше, что полностью снизит производительность. - person Agentlien; 24.05.2013
comment
@beaker: это связано с кешированием на уровне ЦП (предположим, что матрица представляет собой один большой блок памяти), тогда строки кеша являются эффективными строками матрицы, а модуль предварительной выборки может выбрать следующие несколько строк. Если вы переключите доступ, кэш ЦП / предварительная выборка по-прежнему будут работать построчно, в то время как вы открываете столбец за столбцом, падение производительности может быть значительным. - person Matthieu M.; 24.05.2013
comment
@taocp По сути, вам понадобится какой-то флаг, чтобы указать, что он транспонирован, а затем запрос, скажем, (i,j) будет сопоставлен с (j,i) - person Shafik Yaghmour; 24.05.2013
comment
кроме того, если вы передаете матрицу между приложениями, которые не являются одновременно основными по столбцу или не являются одновременно основными по строкам, требуется транспонирование. - person Jack Wasey; 19.03.2018

Некоторые подробности о транспонировании квадратных матриц 4x4 с плавающей запятой (32-битные целые числа мы обсудим позже) с помощью оборудования x86. Полезно начать здесь, чтобы транспонировать большие квадратные матрицы, такие как 8x8 или 16x16.

_MM_TRANSPOSE4_PS(r0, r1, r2, r3) реализуется разными компиляторами по-разному. GCC и ICC (я не проверял Clang) используют 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 инструкции больше, чем реализация GCC, ICC и MSVC. Преимущество состоит в том, что он снижает давление в порту, что может иметь преимущество при некоторых обстоятельствах. В настоящее время все перетасовки и распаковки могут идти только на один конкретный порт, тогда как смеси могут идти на любой из двух разных портов.

Я попытался использовать 8 перетасовок, таких как MSVC, и преобразовать их в 4 перетасовки + 8 смесей, но это не сработало. Еще пришлось использовать 4 распаковки.

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

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

person Z boson    schedule 28.12.2016
comment
Вы можете использовать shufps для целочисленных данных. Если вы много перетасовываете, возможно, стоит проделать все это в домене FP для shufps + blendps, особенно если у вас нет столь же эффективного AVX2 vpblendd. Кроме того, на оборудовании семейства Intel SnB нет дополнительной задержки обхода для использованияshufps между целочисленными инструкциями, такими как paddd. (Однако существует задержка обхода для смешивания blendps с paddd, согласно тестированию SnB Агнера Фога.) - person Peter Cordes; 29.12.2016
comment
@PeterCordes, мне нужно еще раз просмотреть изменения домена. Есть ли какая-то таблица (возможно, ответ на SO), в которой суммируются штрафы за смену домена для Core2-Skylake? В любом случае я больше подумал об этом. Теперь я понимаю, почему вы и wim продолжали упоминать vinsertf64x4 в моем ответе на транспонирование 16x16 вместо vinserti64x4. Если я читаю, а затем пишу матрицу, то, конечно, не имеет значения, использую ли я домен с плавающей запятой или целочисленный домен, поскольку транспонирование - это просто перемещение данных. - person Z boson; 30.12.2016
comment
В таблицах Агнера перечислены домены для каждой инструкции для Core2 и Nehalem (и AMD, я думаю), но не для семейства SnB. В руководстве по микроархитектуре Agner просто есть параграф, в котором говорится, что он снижается до 1c, а часто и до 0 на SnB, с некоторыми примерами. Я думаю, что в руководстве по оптимизации Intel есть таблица, но я не пытался ее разобрать, поэтому не помню, сколько в ней деталей. Я помню, что было не совсем очевидно, к какой категории будет относиться данная инструкция. - person Peter Cordes; 30.12.2016
comment
Даже если вы не просто записываете обратно в память, это всего лишь 1 дополнительный такт на всю транспонирование. Дополнительная задержка для каждого операнда может происходить параллельно (или в шахматном порядке), когда потребитель транспонирования начинает читать регистры, записанные путем перемешивания или смешивания. Выполнение вне очереди позволяет запускать первые несколько FMA или что-то еще, в то время как последние несколько перемешиваний заканчиваются, но нет цепочки задержек dypass, только дополнительная, самое большее, одна. - person Peter Cordes; 30.12.2016
comment
Ничв ответ! В руководстве по оптимизации архитектуры Intel 64-ia-32, таблица 2-3, перечислены задержки обхода для Skylake, возможно, это вас заинтересует. Таблица 2-8 для Haswell выглядит иначе. - person wim; 30.12.2016
comment
Думаю, на Skylake vinsertf64x4 и vinserti64x4 взаимозаменяемы. У меня не было причин упоминать одно или другое. Я просто думал о 64x4 битах данных. - person wim; 30.12.2016

Если размер массивов известен заранее, мы могли бы использовать объединение для нашей помощи. Нравится-

#include <bits/stdc++.h>
using namespace std;

union ua{
    int arr[2][3];
    int brr[3][2];
};

int main() {
    union ua uav;
    int karr[2][3] = {{1,2,3},{4,5,6}};
    memcpy(uav.arr,karr,sizeof(karr));
    for (int i=0;i<3;i++)
    {
        for (int j=0;j<2;j++)
            cout<<uav.brr[i][j]<<" ";
        cout<<'\n';
    }

    return 0;
}
person Sandeep K V    schedule 01.08.2019
comment
Я новичок в C / C ++, но это выглядит гениально. Поскольку union использует разделяемую память для своих членов, вы можете читать эту память по-разному. Таким образом, вы получаете транспонированную матрицу без выделения нового массива. Я прав? - person Doğuş; 28.10.2020

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

демонстрация: http://ideone.com/lvsxKZ

#include <iostream> 
using namespace std;

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

    cout << "A = " << endl << endl;

    // print matrix A
    for (int i=0; i<3; i++)
    {
        for (int j=0; j<3; j++) cout << A[i][j];
        cout << endl;
    }

    cout << endl << "A transpose = " << endl << endl;

    // print A transpose
    for (int i=0; i<3; i++)
    {
        for (int j=0; j<3; j++) cout << A[j][i];
        cout << endl;
    }

    return 0;
}
person Khaled.K    schedule 25.05.2013

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

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)

Конечно, я не стал беспокоиться об управлении памятью, это важная, но другая тема.

person Reza Baram    schedule 17.03.2014
comment
У вас есть накладные расходы на указатель функции, которые необходимо соблюдать при каждом доступе к элементу. - person user877329; 28.08.2014

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

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

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

#include <mkl.h>

void transpose( float* a, int n, int m ) {
    const char row_major = 'R';
    const char transpose = 'T';
    const float alpha = 1.0f;
    mkl_simatcopy (row_major, transpose, n, m, alpha, a, n, n);
}

Для проекта C ++ вы можете использовать Armadillo C ++:

#include <armadillo>

void transpose( arma::mat &matrix ) {
    arma::inplace_trans(matrix);
}
person Jorge Bellon    schedule 21.09.2019

intel mkl предлагает матрицы транспонирования / копирования на месте и не на месте. вот ссылка на документацию. Я бы рекомендовал попробовать неуместную реализацию, так как более быстрая десятка на месте и в документации последней версии mkl есть некоторые ошибки.

person Gennady.F    schedule 18.10.2019

Я думаю, что самый быстрый способ не должен превышать O (n ^ 2), также таким образом вы можете использовать только пространство O (1):
способ сделать это - поменять местами попарно, потому что когда вы транспонируете матрицу то вы делаете следующее: M [i] [j] = M [j] [i], поэтому сохраните M [i] [j] в temp, тогда M [i] [j] = M [j] [i] » , и последний шаг: M [j] [i] = temp. это можно сделать за один проход, поэтому он должен занять O (n ^ 2)

person Fayez Abdlrazaq Deab    schedule 29.05.2013
comment
M [i] [j] = M [j] [i] будет работать, только если это будет квадратная матрица; иначе это вызовет исключение индекса. - person Antony Thomas; 16.03.2015

мой ответ транспонирован из матрицы 3x3

 #include<iostream.h>

#include<math.h>


main()
{
int a[3][3];
int b[3];
cout<<"You must give us an array 3x3 and then we will give you Transposed it "<<endl;
for(int i=0;i<3;i++)
{
    for(int j=0;j<3;j++)
{
cout<<"Enter a["<<i<<"]["<<j<<"]: ";

cin>>a[i][j];

}

}
cout<<"Matrix you entered is :"<<endl;

 for (int e = 0 ; e < 3 ; e++ )

{
    for ( int f = 0 ; f < 3 ; f++ )

        cout << a[e][f] << "\t";


    cout << endl;

    }

 cout<<"\nTransposed of matrix you entered is :"<<endl;
 for (int c = 0 ; c < 3 ; c++ )
{
    for ( int d = 0 ; d < 3 ; d++ )
        cout << a[d][c] << "\t";

    cout << endl;
    }

return 0;
}
person angel    schedule 25.12.2013

person    schedule
comment
Я бы предпочел подумать, что будет быстрее, если вы поменяете два цикла из-за меньшего штрафа за пропуск кеша при записи, чем при чтении. - person phoeagon; 24.05.2013
comment
Это работает только для квадратной матрицы. Прямоугольная матрица - это совсем другая проблема! - person NealB; 24.05.2013
comment
Вопрос просит самый быстрый способ. Это просто способ. Что заставляет вас думать, что он быстрый, не говоря уже о самом быстром? Для больших матриц это приведет к перегрузке кеша и плохой производительности. - person Eric Postpischil; 25.05.2013
comment
@NealB: Как ты это догадаешься? - person Eric Postpischil; 25.05.2013
comment
@EricPostpischil OP спрашивает об относительно большой матрице, поэтому я предполагаю, что они хотели сделать это на месте, чтобы избежать выделения двойной памяти. Когда это сделано, базовые адреса исходной и целевой матриц совпадают. Транспонирование путем переворачивания индексов строк и столбцов работает только для квадратных матриц. Есть методы, позволяющие добиться этого для прямоугольных матриц, но они несколько сложнее. - person NealB; 25.05.2013
comment
@NealB: Эта критика неприменима к этому коду. Этот код не является неправильным для неквадратных матриц. - person Eric Postpischil; 25.05.2013
comment
Этот код подходит для неквадратных матриц (хотя и не очень оптимален). Я думаю, что @EricPostpischil думает об алогизме для транспонирования на месте. Это намного сложнее en.wikipedia.org/wiki/. - person ; 27.05.2013
comment
@raxman: Возможно, вы обратились не к тому человеку или неправильно прочитали заявление о том, что код «не является неправильным». - person Eric Postpischil; 27.05.2013