Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2006.12.24;
Скачать: [xml.tar.bz2];

Вниз

Еще задача по программированию   Найти похожие ветки 

 
Romkin ©   (2006-12-01 12:41) [0]

Наш менеджер принес задачку, решить на Delphi.
Ну наконец-то я увидел нормальное задание для лабораторной по программированию!
Вот:
В целочисленном массиве A из M строк и N столбцов осуществить циклический сдвиг на величину N:
 если N четно, то сдвиг "вверх"
 если N нечетно, то сдвиг "вниз"

Решил, разумеется. Причем оптимальным способом. Потом придет за объяснениями - и не уйдет, пока не поймет, как это работает.
По моим прикидкам, там будет 4 вопроса "почему" :)

А кто-нибудь из форумчан может предложить максимально оптимальное решение? По асимптотике? Задачка-то интересная

Массив объявлен как
type TMatrix = array of array of integer;


 
Курдль ©   (2006-12-01 12:44) [1]


> В целочисленном массиве A из M строк и N столбцов осуществить
> циклический сдвиг на величину N:

"Величина N" равна кол-ву столбцов?!!


 
Romkin ©   (2006-12-01 12:50) [2]


> "Величина N" равна кол-ву столбцов?!!

Ну да, разумеется :)


 
Alx2 ©   (2006-12-01 13:01) [3]

Циклический сдвиг на s элементов. Взял из старых записей-черновиков. Переделывать под описанный случай лень. Просто идея.

const n = 12;
var data : array [0..n-1] of integer;
 k,s,p : integer;
procedure swap(k,l : Integer);
Var tmp : Integer;
Begin
 tmp := data[k];
 data[k] := data[l];
 data[l] := tmp;
End;
begin
for k := low(data) to High(data) do Data[k] := k;
s := 8;
k := 0;
p := 0;
s := s mod n;
while s>0 do
begin
  for p := 0 to s-1 do swap(k+p,n - s + p);
  inc(k,s);
  s := s mod (n - k);
end;
end.


 
Alien1769 ©   (2006-12-01 13:05) [4]

Статейка "Поразрядная сортировка" очень даже рулит :)
Угадал?


 
MBo ©   (2006-12-01 13:05) [5]

A B
C D
E F
должен превратиться в
C D
E F
A B
?


 
Юрий Зотов ©   (2006-12-01 13:06) [6]

> Romkin ©   (01.12.06 12:41)

Сводится к циклическом сдвигу указателей в одномерном "наружном" массиве. Его, в свою очередь, можно реализовать "переносом хвоста в начало" (сдвиг вверх), либо наоборот (сдвиг вниз). Хотя вряд ли это будет оптимальнее, чем просто поменять значения указателей в цикле.


 
Romkin ©   (2006-12-01 13:08) [7]

Alien1769 ©   (01.12.06 13:05) [4] При чем здесь сортировка?! :)
Alx2 ©   (01.12.06 13:01) [3] Некрасиво и неоптимально :) Количество перестановок примерно равно длине массива, умноженной на величину сдвига. Не катит :)
MBo ©   (01.12.06 13:05) [5] Это - сдвиг на две строки вниз. N - четно, надо на две вверх.
E F
A B
C D


 
Alx2 ©   (2006-12-01 13:12) [8]

>Romkin ©   (01.12.06 13:08)

>Количество перестановок примерно равно длине массива,
>умноженной на величину сдвига.

Роман, обидные вещи говорите. Такую фигню я бы написал без "зауми" с mod


 
Romkin ©   (2006-12-01 13:13) [9]

Юрий Зотов ©   (01.12.06 13:06) [6] Циклический сдвиг массива из M элементов на N позиций делается примерно за 2*M обменов элементов (считать лень, +-1). Это - оптимально.


 
Alien1769 ©   (2006-12-01 13:16) [10]


> Alien1769 ©   (01.12.06 13:05) [4] При чем здесь сортировка?
> ! :)

Я имел ввиду твою статью, а там как раз описан  случай приведенныей ЮЗ.

> Сводится к циклическом сдвигу указателей в одномерном "наружном"
> массиве. Его, в свою очередь, можно реализовать "переносом
> хвоста в начало" (сдвиг вверх), либо наоборот (сдвиг вниз).
>  Хотя вряд ли это будет оптимальнее, чем просто поменять
> значения указателей в цикле.


 
Romkin ©   (2006-12-01 13:17) [11]

Alx2 ©   (01.12.06 13:12) [8] О, сорри. Не увидел :) Увидел два цикла. Сколько обменов у тебя?


 
Romkin ©   (2006-12-01 13:47) [12]

Alien1769 ©   (01.12.06 13:16) [10] Здесь удобнее внутри массива все делать, как сказал Зотов, указателями, практически-то это можно трактовать как одномерный массив указателей на массивы :)
Но вот как сдвинуть внутри массива с наименьшими затратами - вот это интересно. Сейчас пытаюсь разобраться с [3] :)
У меня - другой алгоритм.


 
Alx2 ©   (2006-12-01 13:56) [13]

К алгоритму в Alx2 ©   (01.12.06 13:01) [3]

пусть n - число элементов.
пусть s - сдвиг.

Если n>s то незачем нарезать полные обороты по массиву.
Поэтому есть строчка s := s mod n;

Далее.  пусть k = номер элемента, с которого начинается массив.
Перетащив s элементов из хвоста в начало мы добиваемся того, что начало массива в полном порядке. А хвост совпадает с началом.
Это делает строчка
 for p := 0 to s-1 do swap(k+p,n - s + p);

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

Говорим, что наш новый массив теперь начинается с позиции k=k+s
вычислям новый сдвиг с учетом новой длины:  s := s mod (n - k);
И все повторяем снова - элементы хвоста ставятся на свои места, а начало уходит в хвост.
Так повторяем до тех пор, пока новый сдвиг s не станет нулем.

Примерная и (кажется) завышенная оценка количества обменов в худшем случае: n +  log(n) - из интуитивных соображений. Пока не могу дать более строго


 
MBo ©   (2006-12-01 14:00) [14]

M + o(M)  полуобменов

function GCD(A, B: Integer): Integer;
var
 Temp: Integer;
begin
 while (B > 0) do begin
   Temp := A mod B;
   A := B;
   B := Temp;
 end;
 Result := A;
end;

procedure ShiftArrDown(pc: PPointerArray; M, N: Integer);
var
 p: Pointer;
 iCurr, iTo, iFrom, GC, NGC, k: Integer;
begin
 GC := GCD(M, N);
 NGC := M div GC;
 for iCurr := M - 1 downto M - GC do begin
   p := pc[iCurr];
   iTo := iCurr;
   for k := 1 to NGC - 1 do begin
     iFrom := iTo - N;
     if iFrom < 0 then
       iFrom := iFrom + M;
     pc[iTo] := pc[iFrom];
     iTo := iFrom;
   end;
   pc[iTo] := p;
 end;
end;

вызов
 if Odd(N) then
   Shft := N mod M
 else
   Shft := (N * M - N) mod M;
 ShiftArrDown(@A[0], M, Shft);


 
Romkin ©   (2006-12-01 14:13) [15]

Ничего не понимаю :)))
У меня ровно M или M+1 обменов:

procedure ReverseArr(var Arr: TMatrix; FromInd, ToInd: integer);
var
 i: integer;
 T: pointer;
begin
 assert((FromInd >= 0) and (FromInd <= High(Arr)),
   "Начальный индекс должен лежать в границах массива");
 assert((ToInd >= 0) and (ToInd <= High(Arr)),
   "Конечный индекс должен лежать в границах массива");
 if ToInd = FromInd then //Допустимо, но нечего делать
   exit;
 for i := 0 to (ToInd - FromInd) div 2 do
 begin //Внимание! Меняем указатели на массивы!!!
   T := pointer(Arr[FromInd + i]);
   pointer(Arr[FromInd + i]) := pointer(Arr[ToInd - i]);
   pointer(Arr[ToInd - i]) := T;
 end;
end;

procedure RotateLines(var Arr: TMatrix; NumLines: integer);
begin
 assert((NumLines > 0) and (NumLines < Length(Arr)),
   "Смещение должно лежать в границах массива и быть существенным");
 ReverseArr(Arr, 0, NumLines - 1);
 ReverseArr(Arr, NumLines, High(Arr));
 ReverseArr(Arr, 0, High(Arr));
end;

Кажется, я делаю лишние :(
Но не могу понять, вроде процедура реверса у меня правильная!

Но почему у Alx2 обменов гораздо меньше?!


 
Romkin ©   (2006-12-01 14:24) [16]

Ну точно!
procedure ReverseArr(var Arr: TMatrix; FromInd, ToInd: integer);
var
 i: integer;
 T: pointer;
begin
 assert((FromInd >= 0) and (FromInd <= High(Arr)),
   "Начальный индекс должен лежать в границах массива");
 assert((ToInd >= 0) and (ToInd <= High(Arr)),
   "Конечный индекс должен лежать в границах массива");
 if ToInd = FromInd then //Допустимо, но нечего делать
   exit;
 for i := 1 to (ToInd - FromInd + 1) div 2 do
 begin //Внимание! Меняем указатели на массивы!!!
   T := pointer(Arr[FromInd + i - 1]);
   pointer(Arr[FromInd + i - 1]) := pointer(Arr[ToInd - i + 1]);
   pointer(Arr[ToInd - i + 1]) := T;
 end;
end;

Ровно M обменов...


 
Alx2 ©   (2006-12-01 14:25) [17]

>Romkin ©   (01.12.06 14:13) [15]

У Бориса (MBo) их еще меньше, хотя идея та же. Мой код еще можно причесывать дальше.


 
Romkin ©   (2006-12-01 14:32) [18]

Не понимаю все же. Ведь при циклическом сдвиге сдвигаются все элементы. Как может быть число перестановок меньше числа элементов?


 
Игорь Шевченко ©   (2006-12-01 14:35) [19]

MBo ©   (01.12.06 14:00) [14]

Борис, выключи обфускатор :)


 
MBo ©   (2006-12-01 14:41) [20]

>Как может быть число перестановок меньше числа элементов?
может, тебя смутило, что у Alx2  N означает длину массива, а не M, как у тебя?


 
Alx2 ©   (2006-12-01 14:43) [21]

>Romkin ©   (01.12.06 14:32) [18]

>Как может быть число перестановок меньше числа элементов?
Например, обменять местами половинки массивов можно за n/2 обменов.
А это как раз сдвиг на n/2.

Кстати, отсюда еще одна идея по новому алгориму рождается -  в бинарный вид и от него "пляшем" :) Но уже не уверен, что получится лучше.


 
Юрий Зотов ©   (2006-12-01 14:45) [22]

Можно решить задачу в три строчки: SetLength и 2 раза Move. Такое решение считается оптимальным, или нет?


 
clickmaker ©   (2006-12-01 14:47) [23]


> [22] Юрий Зотов ©   (01.12.06 14:45)

это не спортивно )


 
Alx2 ©   (2006-12-01 14:48) [24]

>Юрий Зотов ©   (01.12.06 14:45)

move работает за  время пропорциональное количеству элементов.


 
Юрий Зотов ©   (2006-12-01 14:49) [25]

> clickmaker ©   (01.12.06 14:47) [23]

Зато дешево, надежно, и практично...
(с) А. Папанов

:о)


 
Romkin ©   (2006-12-01 14:49) [26]

Alx2 ©   (01.12.06 14:43) [21] Млин. Перепутал обмены с обращениями... Я и в самом начале спутал это, когда говорил о 2N :(


 
Romkin ©   (2006-12-01 14:50) [27]

Юрий Зотов ©   (01.12.06 14:49) [25] Двумерный массив сразу весь move? Эт как?


 
Юрий Зотов ©   (2006-12-01 14:54) [28]

> Romkin ©   (01.12.06 14:50) [27]

Зачем двумерный? Внутренние массивы вообще на трогаем, а в наружном, одномерном переставляем указатели на внутренние.

Точнее, не в нем самом, а делаем его новый экземпляр. Ставим ему ту же длину, потом копируем в него из исходного массива один кусок указателей, потом другой.


 
Romkin ©   (2006-12-01 15:02) [29]

Юрий Зотов ©   (01.12.06 14:54) [28] Угу. И начинаются игры с финализацией... Сложновато


 
evvcom ©   (2006-12-01 15:08) [30]

> [29] Romkin ©   (01.12.06 15:02)
> Угу. И начинаются игры с финализацией

Да чего там финализировать? FillChar нулями и усе.

> [28] Юрий Зотов ©   (01.12.06 14:54)
> Точнее, не в нем самом, а делаем его новый экземпляр.

Думаю и в нем самом можно.
type TVector = array of Pointer;
var
 Matrix: TMatrix;
 Vector: TVector absolute Matrix;

а дальше SetLength, 2 Move и отладчик с CPU Window :)))


 
Юрий Зотов ©   (2006-12-01 15:09) [31]

> Romkin ©   (01.12.06 15:02) [29]

А это уже второй вопрос, профессор.
:о)

ТЗ выполнено, а дополнительные пожелания заказчика готов удовлетворить в дополнительные сроки и за дополнительную плату.
:о)


 
evvcom ©   (2006-12-01 15:13) [32]

> [30] evvcom ©   (01.12.06 15:08)
> Да чего там финализировать? FillChar нулями и усе.

Кстати, если этот массив будет TVector, то и FillChar не нужен.


 
Alx2 ©   (2006-12-01 19:17) [33]

И вот, дошли руки:
Оценка для [3]
Максимальное число обменов = n
Минимальное = n/2
И сложность такая же, как в алгоритме MBo ©   (01.12.06 14:00)



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

Форум: "Прочее";
Текущий архив: 2006.12.24;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.053 c
2-1165396893
Roman_ln
2006-12-06 12:21
2006.12.24
Если в делфи процедуры работающие с датой?


2-1165217136
maxXP
2006-12-04 10:25
2006.12.24
Положение элемента


5-1145509652
DimaBr
2006-04-20 09:07
2006.12.24
Left Top компонета


15-1164875852
pasha_golub
2006-11-30 11:37
2006.12.24
Вычисление булевых выражений


6-1154444611
Gerb
2006-08-01 19:03
2006.12.24
Возможно ли редактирование файла по фтп без закачки?





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский