Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.04.09;
Скачать: CL | DM;

Вниз

Понедельничная задачка ;)   Найти похожие ветки 

 
MBo ©   (2006-03-20 09:40) [0]

Дана двумерная матрица целых чисел размером MxN, записанная в одномерном массиве. Разработать процедуру транспонирования матрицы в том же массиве (inplace) без использования дополнительных массивов.
прототип:
procedure TransposeArr(var A: array of Integer; Rows, Cols: Integer);

P.S.
Трaнcпонирование матрицы A (MxN) переводит ее в матрицу B (NxM), в которой элементы B[y,x] = A[x,y]. Для квадратной - это отражение относительно главной диагонали
Пример:
00 01 02
10 11 12
будет лежать в одномерном массиве  00 01 02 10 11 12
после транспозиции
00 10
01 11
02 12


 
palva ©   (2006-03-20 10:00) [1]

Транспонирование это некоторая перестановка одномерного массива. Любая перестановка разлагается на циклы: цикл это кольцевая последовательность элементов массива, каждый элемент которой при перестановке переходит в следующий. Цикл нетрудно подвергнуть перестановке, (т.е. переместить каждый элемент в следующий) без дополнительных массивов. Если бы удалось разработать алгоритм, который по числам M и N последовательно порождает по одному представителю каждого цикла (его место i, j) то задача была бы решена.

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


 
wal ©   (2006-03-20 10:47) [2]

А можно без перестановок, а просто "перевычислением" ниужного индекса?


 
McSimm ©   (2006-03-20 11:58) [3]

Сделал ! :))

http://www.delphimaster.ru/transpose.php
Все по условию, только перестановки внутри одномерного массива, никаких встроенных функций, кроме вывода


 
McSimm ©   (2006-03-20 12:00) [4]

поспешил, не работает для произвольных M N
:(


 
Думкин ©   (2006-03-20 12:02) [5]

> McSimm ©   (20.03.06 11:58) [3]

M(1..9): <input type="text" name="m" value="5"><br>
N(1..9): <input type="text" name="n" value="4"><br>

1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20

1 6 11 16
2 3 4 17
12 7 5 18
8 13 9 19
10 14 15 20
1 , 6 , 11 , 16 , 2 , 3 , 4 , 17 , 12 , 7 , 5 , 18 , 8 , 13 , 9 , 19 , 10 , 14 , 15 , 20


Вроде - не то.


 
TStas ©   (2006-03-20 12:06) [6]

А чего сложного-то? Функция, переводящая двуменый индекс в одмометный. Дальше надо писать?


 
McSimm ©   (2006-03-20 12:08) [7]


> TStas ©   (20.03.06 12:06) [6]

Да, именно то что дальше и надо :)


 
TStas ©   (2006-03-20 12:15) [8]

А дальше ОДНА переменная, а не массив. ТРанспонируем индексы двумерные, получаем новый одномерный. То, что стояло в массиве под этим индексом пишем в переменную, ну, короче, меняем местами. Вот и все. И так всю матрицу.  НЕ обязательно транспонировать и не обязательно двумерный даже. ЧУдес не бывает. Все, что в компе раположено в памяти, т. е. Array[Cardinal] of byte И деревья и вообще все.


 
TStas ©   (2006-03-20 12:20) [9]

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


 
MBo ©   (2006-03-20 12:31) [10]

>wal ©   (20.03.06 10:47) [2]
>А можно без перестановок, а просто "перевычислением" ниужного индекса?
Ну так неинтересно ;)

>McSimm
при 3х3 - неверный результат

>TStas
Дан именно одномерный массив. Но и для двумерного неквадратного не совсем тривиально произвести транспонирование на месте.

Могу дать простенькую заготовку, использованную мной для тестирования:

procedure TForm4.Button1Click(Sender: TObject);

 procedure ShowAsMatr(Arr: array of Integer; Rows, Cols: Integer);
 var
   x, y: Integer;
   s: string;
 begin
   Memo1.Lines.Add("");
   for y := 0 to Rows - 1 do begin
     s := "";
     for x := 0 to Cols - 1 do
       s := s + Format("%.4d  ", [Arr[y * Cols + x]]);
     Memo1.Lines.Add(s);
   end;
 end;

 procedure FillMatr(var Arr: array of Integer; Rows, Cols: Integer);
 var
   x, y: Integer;
 begin
   for y := 0 to Rows - 1 do
     for x := 0 to Cols - 1 do
       Arr[y * Cols + x] := y * 100 + x;
 end;

 procedure TransposeArr(var A: array of Integer; Rows, Cols: Integer);
 begin
// ?????????????????????
 end;

const
 H = 4;
 W = 5;
var
 Arr: array of Integer;
begin
 SetLength(Arr, W * H);
 FillMatr(Arr, H, W);
 ShowAsMatr(Arr, H, W);
 TransposeArr(Arr, H, W);
 ShowAsMatr(Arr, W, H);
end;


 
McSimm ©   (2006-03-20 12:31) [11]


> TStas ©

ну, короче, все ясно :)


 
Jeer ©   (2006-03-20 12:38) [12]

srcc.msu.su :))


 
McSimm ©   (2006-03-20 12:44) [13]

Мда, задумка у меня была красивая, но раьотает только при N<3 :))))
Жаль времени нет. Но я ее добью потом


 
han_malign ©   (2006-03-20 13:07) [14]


> без использования дополнительных массивов.

- а временная перепенная, под хранение элемента - считается частным случаем дополнительного массиав с размером 1???


 
MBo ©   (2006-03-20 13:16) [15]

>han_malign ©   (20.03.06 13:07) [14]
Несколько переменных можно использовать.

Формально - допускается использование O(1) памяти, но не O(N)


 
palva ©   (2006-03-20 13:58) [16]

Могу привести пример с использованием дополнительного бита. То есть под целые элементы матрицы разрешается использовать 31 бит. Это не разрешается условием но обозначает проблему. Константы m и n можно задать произвольными целыми положительными.

{$APPTYPE CONSOLE}
const
 m = 3;
 n = 4;
var
 a0: array[1..m*n] of Integer;  
 a1: array[1..m, 1..n] of Integer absolute a0;
 i, j, k: Integer;

procedure TransposeArr(var A:array of Integer; Rows, Cols: Integer);
var
 n, i, j, k, s, s_new, temp: Integer;
begin
 n := Rows*Cols;
 for k := 0 to n - 1 do A[k] := A[k] Shl 1;
 for k := 0 to n - 1 do begin
   if not odd(A[k]) then begin
     temp := A[k];
     s := k;
     repeat
       j := s mod Cols;
       i := s div Cols;
       s_new := j*Rows+i;
       if s_new<>k then begin
         A[s] := A[s_new] Or 1;
         s := s_new
       end else begin
         a[s] := temp Or 1;
         break
       end
     until false
   end
 end;
 for k := 0 to n - 1 do A[k] := A[k] Shr 1;
end;

begin
 for k :=1 to n*m do a0[k] := k;
 for i := 1 to m do begin
   for j := 1 to n do
     Write(a1[i, j]:5);
   WriteLn
 end;
 TransposeArr(a0, m, n);  
 Writeln;
 for i := 1 to m do begin
   for j := 1 to n do
     Write(a1[i, j]:5);
   WriteLn
 end
end.


 
palva ©   (2006-03-20 16:24) [17]

Что-то я не совсем то сделал.


 
MBo ©   (2006-03-20 16:32) [18]

>palva ©   (20.03.06 16:24) [17]
>Что-то я не совсем то сделал.

Да вроде бы верно, только Cols и Rows наоборот, как в фортране


 
palva ©   (2006-03-20 16:45) [19]

Да. Вот так надо

{$APPTYPE CONSOLE}
const
 m = 12;
 n = 5;
var
 a0: array[1..m*n] of Integer;  
 a1: array[1..m, 1..n] of Integer absolute a0;
 a2: array[1..n, 1..m] of Integer absolute a0;
 i, j, k: Integer;

procedure TransposeArr(var A:array of Integer; Rows, Cols: Integer);
var
 n, i, j, k, s, s_new, temp: Integer;
begin
 n := Rows*Cols;
 for k := 0 to n - 1 do A[k] := A[k] Shl 1;
 for k := 0 to n - 1 do begin
   if not odd(A[k]) then begin
     temp := A[k];
     s := k;
     repeat
       j := s mod Rows;
       i := s div Rows;
       s_new := j*Cols+i;
       if s_new<>k then begin
         A[s] := A[s_new] Or 1;
         s := s_new
       end else begin
         a[s] := temp Or 1;
         break
       end
     until false
   end
 end;
 for k := 0 to n - 1 do A[k] := A[k] Shr 1;
end;
begin
 for k :=1 to n*m do a0[k] := k;
 for i := 1 to m do begin
   for j := 1 to n do
     Write(a1[i, j]:5);
   WriteLn
 end;
 TransposeArr(a0, m, n);  
 Writeln;
 for i := 1 to n do begin
   for j := 1 to m do
     Write(a2[i, j]:5);
   WriteLn
 end
end.

Google по "rectangular matrix transpose inplace" дает ссылки на ACM, но там сами статьи можно читать после платной регистрации. Может быть, и есть какие-то теорчисловые исследования на эту тему.


 
Jeer ©   (2006-03-20 17:12) [20]

palva ©   (20.03.06 16:45) [19]

Еще раз:
Не самый лучший вариант, но - идея понятна дб., тем более, что двухвариантна - Fortran & C

http://www.srcc.msu.su/num_anal/lib_na/cat/cat522.htm


 
McSimm ©   (2006-03-20 17:23) [21]

Я ее все-таки сделал !
http://www.delphimaster.ru/transpose.php?m=4&n=3


 
McSimm ©   (2006-03-20 17:30) [22]

Ну, нет у меня Delphi, что поделать :)
Может и не совсем оптимально, но мне нравится, красиво.

Борис, спасибо за задачку :) !

function get_index($i, $j, $m)
{
   return $i*$m+$j;
}

function transpose(&$a, $m, $n)
{
   for ($i=0; $i<$n; $i++)
   {
       for ($j=0; $j<$m; $j++)
       {
           $k1 = get_index($i, $j, $m);
           $k2 = get_index($k1 % $n, (int)($k1 / $n), $m);
           while ($k1 > $k2) $k2 = get_index($k2 % $n, (int)($k2/$n), $m);
           if ($k1 != $k2)
           {
               $c = $a[$k1];
               $a[$k1] = $a[$k2];
               $a[$k2] = $c;
           }
       }
   }
}


 
MBo ©   (2006-03-20 17:30) [23]

>palva
Судя по обсуждению:
http://groups.google.ru/group/fido7.ru.algorithms/browse_thread/thread/3dad00d1e4052377/b41999effedc054a?q=%D1%82%D1%80%D0%B0%D0%BD%D1%81%D0%BF%D0%BE%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B8&rnum=2#b41999effedc054a
хорош по скорости метод с использованием O(N) дополнительной памяти для отметки пройденных элементов и отдельной обработкой квадратной части,  
http://www.srcc.msu.su/num_anal/lib_na/cat/am/amtgr.htm

В принципе, для меня задача практического значения не имеет, но при её решении мне было полезно то, что я осознал общность метода для класса задач перестановок в массиве. Родственная задачка была как-то на переупорядочение сортированного массива "горкой", т.е. получение битонической последовательности. Тогда помучиться пришлось, зато теперь легче ;) Я не сохранял признак обработанности, поэтому в начале цикла проверка по цепочкам пробегает. Возможно, введение счетчика обработанных узлов ускорит процесс, но для простоты я его убрал. Изменяя ReverseIndex, можно хоть крутить матрицу, хоть еще что-то делать:

 procedure TransposeArr(var A: array of Integer; Rows, Cols: Integer);
 var
   iStart, iForward, iTemp, Total, aStored: Integer;

   function ReverseIndex(indx: Integer): Integer;
   begin
     Result := (indx mod Rows) * Cols + indx div Rows;
   end;

 begin
   Total := Rows * Cols;
   iStart := 0;
   while iStart < Total do begin
     iForward := ReverseIndex(iStart);
     while iForward > iStart do
       iForward := ReverseIndex(iForward);
     if iForward >= iStart then begin
       iTemp := iStart;
       aStored := a[iStart];
       iForward := ReverseIndex(iTemp);
       while iForward <> iStart do begin
         a[iTemp] := a[iForward];
         iTemp := iForward;
         iForward := ReverseIndex(iTemp);
       end;
       a[iTemp] := aStored;
     end;
     Inc(iStart);
   end;
 end;


 
MBo ©   (2006-03-20 17:34) [24]

>Ну, нет у меня Delphi
Ну общий смысл понятен , только от $$ в глазах рябит ;))


 
han_malign ©   (2006-03-20 18:02) [25]


> ReverseIndex(iTemp);


SubTotal:= Rows * Cols - 1;
..........
ReverseIndex:= (Cols * iTemp) mod SubTotal;

- должно быть быстрее...
(имеет строгое доказательство)


 
MBo ©   (2006-03-20 18:09) [26]

>- должно быть быстрее...
Ну можно и divmod использовать или ассемблерное деление, но это не так существенно.


 
MBo ©   (2006-03-21 08:07) [27]

Из любопытства ради сравнения быстродействия на скорую руку перевел фортрановский исходник AMTGR из БЧА НИВЦ (с ошибками, но на сравнение это не влияет).
Примерное отношение времен выполнения
AMTGR: Palva: MBo

Маленькие матрицы(m,n~10):
1:  1:  2

Большие (m,n~100-300) почти квадратные:
1:  1.5:  5

Большие вытянутые m/n=3:
1:  3:   20

Большие сильно вытянутые m/n=10:
1:  1.5:   9


 
Jeer ©   (2006-03-21 09:39) [28]

Впечатляет:)


 
Danilka ©   (2006-03-21 15:03) [29]

классная задачка :)
полголовы уже сломал, зашел в тупик, ищу выход.
пытаюсь сделать чтобы цикл был не больше чем Rows * Cols - 2


 
McSimm ©   (2006-03-21 15:21) [30]


> Danilka ©   (21.03.06 15:03) [29]

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

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


 
Danilka ©   (2006-03-21 15:23) [31]

[30] McSimm ©   (21.03.06 15:21)
> хм... без дополнительного массива флаговых данных ?
> у меня работал такой алгоритм, но сбивался при увеличении
> матрицы

Ага, у меня тоже сбивается, вот хочу рассчитать, чтоб не сбивался. :)

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

Думаю, это не совсем честно. :)


 
McSimm ©   (2006-03-21 15:49) [32]


> Думаю, это не совсем честно. :)

а мы тихонько, никто не узнает :)))

не уверен, что получилось бы, но на самом деле было бы интересно попробовать.



Страницы: 1 вся ветка

Текущий архив: 2006.04.09;
Скачать: CL | DM;

Наверх




Память: 0.56 MB
Время: 0.028 c
2-1143027901
ales
2006-03-22 14:45
2006.04.09
чтение строки


2-1143303978
XiAndr
2006-03-25 19:26
2006.04.09
Новые страницы в TWebBrowser


15-1142502725
Esu
2006-03-16 12:52
2006.04.09
ICQ проблемы


2-1143460798
dest81
2006-03-27 15:59
2006.04.09
Материнская плата


6-1135589718
syned
2005-12-26 12:35
2006.04.09
WinSock приходят неверные данные