Главная страница
    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.54 MB
Время: 0.018 c
4-1137618293
WindowsExplorer
2006-01-19 00:04
2006.04.09
Подскажите программу, которая исследует другие окна.


2-1142951680
Dest81
2006-03-21 17:34
2006.04.09
Interbase


1-1141311245
Andy BitOff
2006-03-02 17:54
2006.04.09
Обработка изображения. Метод.


2-1143473111
SamProf
2006-03-27 19:25
2006.04.09
Как можно перечислить все диски?


11-1124333832
DmiSb
2005-08-18 06:57
2006.04.09
Form.Show





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский