Транзирование матрицы на месте

Можно ли транспонировать матрицу (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];
      }
    }
 }

не применяется к одному массиву, если только матрица не является квадратной матрицей. Если нет, то какой минимальный объем дополнительной памяти нужен?

EDIT: Я уже пробовал все ароматы

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]

Ответы

Ответ 1

Вдохновленный Википедия - После описания алгоритмов цикла, я придумал следующую реализацию на С++:

#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

представлен в упорядочивании строк {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 of transpose представляет строку rowize, columnize 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 < (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. Процедура продолжается с этим циклом до тех пор, пока все циклы не будут транспонированы.

Ответ 2

Проблема заключается в том, что задача задана некорректно. Если бы вы имели в виду "одно и то же" использование одной и той же матрицы, это правильная задача. Но когда вы говорите о записи в одну и ту же область в памяти, "матрица представлена ​​как один массив размером m * n", вы должны добавить, как она представлена ​​там. В противном случае достаточно ничего изменить, кроме функции, которая читает эту матрицу - просто меняйте индексы в ней.

Вы хотите перенести представление матрицы в память так, чтобы функция чтения/установки для этой матрицы по индексам оставалась той же. Не так ли?

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

Если мы установим эти два недостающих условия, задача станет правильной и не составит труда решить.

Просто мы должны взять каждый элемент матрицы по линейному индексу, найти его пару строк/столбцов, перенести его, найти другой результирующий линейный индекс и поместить значение в новое место. Проблема в том, что преобразование автосимметрично только в случае квадратных матриц, поэтому на самом деле это невозможно сделать на сайте. Или это могло бы, если мы найдем всю карту преобразования индекса и позже будем использовать ее на матрице.

Начальная матрица A:
  m - количество строк
  n - количество столбцов
  нм - количество элементов
  li - линейный индекс
  i - номер столбца
  j - номер строки

результирующая матрица B:
  lir - результат линейного индекса

Преобразование массива trans

//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 [] array.

Ответ 3

Выполнение этого в общем случае требует определенных усилий. Неквадратные и внеаудиторные алгоритмы различаются. Сэкономьте себе много усилий и просто используйте FFTW. Я ранее подготовил более полную запись, включая пример кода, по этому вопросу.