Транспонирование матрицы по месту - PullRequest
17 голосов
/ 10 февраля 2012

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

Ответы [ 3 ]

17 голосов
/ 17 февраля 2012

Вдохновленный Википедией - Следуя описанию алгоритма циклов , я придумал следующую реализацию 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

представлена ​​в упорядочении по основной строке {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 из transpose представляет размер строки, размер столбца 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, в противном случае </p>

Мы можем выделить четыре цикла в этой последовательности: { 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 голосов
/ 15 февраля 2012

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

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

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

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

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

Начальная матрица A:
м- количество рядов
n- количество столбцов
нм - количество элементов
li - линейный индекс
я - номер столбца
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 [] как функцию.

0 голосов
/ 05 марта 2013

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...