Oberon space

General Category => Общий раздел => Тема начата: ilovb от Ноябрь 22, 2013, 03:07:30 pm

Название: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 22, 2013, 03:07:30 pm
Тема перекликается с http://oberspace.dyndns.org/index.php/topic,591.0.html
Яркий пример на тему "ниасилил while (и линейный поиск)"

Выдержка из http://kpolyakov.blogspot.ru/2012/10/break-break.html

Цитировать
Пример 5. Разработать функцию, которая определяет, если ли в квадратной матрице элемент, равный заданному значению.

Будем предполагать, что введен тип данных
type Matrix = array[1..N,1..N] of integer;
Тогда функцию можно написать так:
function Find(const A: Matrix; X: integer): boolean;
var i, j: integer;
begin
  Result := True;
  for i:=1 to N do
    for j:=1 to N do
      if A[i,j] = X then exit;
  Result := False
end;
Здесь оператор exit фактически выполняет роль break для вложенного цикла. С формальной точки зрения у функции два выхода. В общем, «я своим ученикам никогда такое не зачту» — уже слышу я от учителей информатики.

А давайте подумаем, какие альтернативы? Можно вот так:
function Find(const A: Matrix; X: integer): Boolean;
var i, j: integer;
label finish;
begin
  Result := True;
  for i:=1 to N do
    for j:=1 to N do
      if A[i,j] = X then goto finish;
  Result := False;
finish:
end;
Но тут вообще «криминал» — метка и goto! Хотя по сути ничего не изменилось, тот же выход из вложенного цикла.

Конечно, здесь можно использовать исключение:
function Find(const A: Matrix; X: integer): Boolean;
var i, j: integer;
begin
  Result := False;
  try
    for i:=1 to N do
      for j:=1 to N do
        if A[i,j] = X then
          raise Exception.Create('');
  except
    Result := True
  end
end;
Но ведь это далеко не «исключительная ситуация», поэтому по смыслу исключения здесь всё-таки не особо уместны. Кроме того, нужно учитывать, что при обработке исключений выполняется большое число машинных команд, что снижает эффективность программы.

Любителей «чистого» структурного программирования наверняка устроит такой вариант решения:
function Find(A: Matrix; X: integer): Boolean;
var i, j: integer;
begin
  Result := False;
  for i:=1 to N do
    for j:=1 to N do
      if A[i,j] = X then Result := True
end;
Но, к сожалению, по эффективности он не может составить конкуренцию предыдущим, так как в любом случае рассматриваются все N2 элементов массива, даже если элемент A[1,1] равен X.

Название: Re: Поиск в матрице n*n
Отправлено: vlad от Ноябрь 22, 2013, 05:01:46 pm
Яркий пример на тему "ниасилил while (и линейный поиск)"

И чего? :) Лично я предпочту пройтись по этой матрице std::find. Но если уж слишком много для этого писать (писатель матрицы не позаботился об итераторах), то тогда в отдельную функцию с двумя return.

Цикл Дейкстры напишу только если настроение поглумиться :)
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 22, 2013, 05:09:50 pm
Тут не нужен никакой цикл Дейкстры. Обычный лин.поиск. Просто инкремент нужно делать не в одном измерении, а в двух.
PROCEDURE Inc2d(VAR d1, d2: INTEGER);
BEGIN
   INC(d2);
   IF d2 = n THEN
      d2 := 0;
      INC(d1);
   END;
END Inc2d;

i := 0; j := 0;
WHILE (i < n) & (m[i, j] # x) DO
   Inc2d(i, j);
END;
IF i < n THEN
   // нашли
END;
Название: Re: Поиск в матрице n*n
Отправлено: vlad от Ноябрь 22, 2013, 05:43:24 pm
Тут не нужен никакой цикл Дейкстры. Обычный лин.поиск. Просто инкремент нужно делать не в одном измерении, а в двух.

Не, VAR-параметры это моветон. Тем более для индексов.
ИМХО, у тебя, собственно, и получился завуалированный ЦД.
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 22, 2013, 05:47:14 pm
Не, VAR-параметры это моветон. Тем более для индексов.

С каких это пор?  :)

INC() тоже моветон?
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 22, 2013, 05:50:44 pm
ИМХО, у тебя, собственно, и получился завуалированный ЦД.

Думаю, что многие циклы "завуалированный ЦД"  ;)
90 процентов чего угодно (http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BA%D0%BE%D0%BD_%D0%A1%D1%82%D0%B0%D1%80%D0%B4%D0%B6%D0%BE%D0%BD%D0%B0)
Название: Re: Поиск в матрице n*n
Отправлено: vlad от Ноябрь 22, 2013, 06:56:30 pm
Не, VAR-параметры это моветон. Тем более для индексов.

С каких это пор?  :)

С тех пор как я ознакомился с хаскелем :) Точно не помню, но примерно в то время пришло осознание, что результат должен возвращаться как результат, а не в виде изменяемых аргументов.
А до этого была просто неудовлетворенность существующим синтаксисом C++ (причем в ББ/обероне точно такая же проблема) - в точке вызова не видно, что аргументы передаются по ссылке и будут изменены.

INC() тоже моветон?

Да. ++ намного лучше ;) В крайнем случае  i += 1 :)

P.S. NEW - тоже дурной. Запись p := NEW смотрится намного естественнее.

P.S.S. Не говоря о том, что не сильно оптимизирующий компилятор породит ужасный код для инкремента индексов из твоего примера.
Название: Re: Поиск в матрице n*n
Отправлено: vlad от Ноябрь 22, 2013, 07:02:33 pm
в точке вызова не видно, что аргументы передаются по ссылке и будут изменены.

В C# это фикснули, но для меня было уже поздно :) Для меня, теперь испорченного хаскелем, возврат результата как результата все равно лучше.
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 22, 2013, 07:12:29 pm
Ну я пока функциональщиной не заболел.  :) Так что воспринимаю нормально. Помечать не мешало бы, да.
В любом случае Inc2d написана только для иллюстрации схемы цикла.
Название: Re: Поиск в матрице n*n
Отправлено: valexey_u от Ноябрь 22, 2013, 10:59:39 pm
Многомерные массивы в языке - моветон :-) Не было бы их, и проблемы не было бы.
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 23, 2013, 08:59:52 am
Матрицы - это моветон. Не было бы их, и проблемы не было бы.
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 23, 2013, 10:55:25 am
Чтобы не казалось, что я только других умею дерьмом поливать.
Пожалста: https://code.google.com/p/1c-toolkit/source/browse/lua/cf_tools/cf_inside.lua#108

Прекрасно помню почему так сделал - было лень писать while  :(
Название: Re: Поиск в матрице n*n
Отправлено: Geniepro от Ноябрь 23, 2013, 12:28:13 pm
i := 0; j := 0;
WHILE (i < n) & (m[i, j] # x) DO
   Inc2d(i, j);
END;
IF i < n THEN
   // нашли
END;

Не понимаю я такого паттерна:
i := 0;
while i < len(arr) and not p(arr[i]) do
  i := i + 1
end;
if i < len(arr) then
  -- нашли!
end;
Всегда делаю так:
i := 0; found := false;
while i < len(arr) and not found do
  if p(arr[i]) then
    found := true
  else
    i := i + 1
end;
if found then
  -- нашли!
end;
или так (учитывая что на паскалях я вапще-то не работаю лет 10):
bool found = false;
for (int i = 0; i < arr.Length && !found; i++)
{
    if (p(arr[i])) found = true;
}
if (found)
{
    // нашли!
}
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 23, 2013, 04:00:38 pm
Из той же оперы: http://ic.asf.ru/~/docs/cpp/cppd_ifstruct.htm

Цитировать
Хотя оператор goto как метод управления программой давно "впал в немилость", он порой оправдывает свое существование. Одним из таких особых случаев является выход из глубоко вложенной процедуры. Рассмотрим, например, следующий фрагмент программы.

int i, j, k;
int stop = 0;

for(i=0; i<100 && istop; i++) {
   for(j=0; j<10 && istop; j+ + ) {
      for(k=0; k<20; k++) {
         // ...
         if(something()) {
            stop = 1;
            break;
         }
      }
   }
}
Переменная stop используется для отмены двух внешних циклов при условии возникновения некоторого программного события. Однако все же лучше достичь того же эффекта с помощью оператора goto.

int i, j, k;

for(i=0; i<100; i++) {
   for(j=0; j<10; j + +) {
      for(k=0; k<20; k++) {
         // . ..
         if(something()) {
            goto done;
         }
      }
   }
}

done: // ...
Как видите, используя оператор goto, можно обойтись без дополнительных затрат ресурсов, которые связаны с повторением проверки значения переменной stop в предыдущем варианте.
Название: Re: Поиск в матрице n*n
Отправлено: Kemet от Ноябрь 23, 2013, 04:02:45 pm
Не понимаю я такого паттерна:
вот здесь-то и пригодился бы цикл-паук
Название: Re: Поиск в матрице n*n
Отправлено: Geniepro от Ноябрь 23, 2013, 04:31:00 pm
int i, j, k;

for(i=0; i<100; i++) {
   for(j=0; j<10; j + +) {
      for(k=0; k<20; k++) {
         // . ..
         if(something()) {
            goto done;
         }
      }
   }
}

done: // ...
Как видите, используя оператор goto, можно обойтись без дополнительных затрат ресурсов, которые связаны с повторением проверки значения переменной stop в предыдущем варианте.

В Аде для этого используется выход из именованного цикла:
outer_loop:
for i in (0..99) loop
   for j in (0..9) loop
      for k in (0..19) loop
         -- ...
         exit outer_loop when something();
      end loop;
   end loop;
end loop outer_loop;
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 23, 2013, 05:51:23 pm
Попытался найти вариант где без break никак нельзя.
Нашел это(Java):
Цитировать
int a=...;
int b=...;
int c=...;
m1:
for(int i=0; i<100; i++) {
   int d = 0;
   for(int j=0; j<100; j++) {
      if ((a+i) % (b+j) = c) {
         a = 3*a;
         b = 4*i;
         c = 2+j;
      }
      if (b*c % a == 0) {
          break m1;
      }
      b++;
      d = (a + b)  % c;
      if (a*b % с == 0) {
         
          break;
      }
   }
   if (d = 0) sout("yyy");
}


Как насчёт поупражняться в рефакторинге такого кода?
Про break и continue в циклах (http://www.rsdn.ru/forum/java/1432379.flat)

OK. Попробуем завернуть в линейный поиск.
Вникать в алгоритм не стал. Но на первый взгляд преобразуется так(Lua):
function calc(i, j, a, b, c)
   if (a + i) % (b + j) == c then
      a = 3 * a
      b = 4 * i
      c = 2 + j
   end
   return a, b, c
end

local i, j = 0, 0
a, b, c = calc(i, j, a, b, c)

while (i < 100) and (b * c % a ~= 0) do
     
   b = b + 1
   d = (a + b) % c
   
   j = j + 1
   if (j == 100) or (a * b % c == 0) then
      j = 0
      i = i + 1
      if d == 0 then
         print 'yyy'
      end
      d = 0
   end

   a, b, c = calc(i, j, a, b, c)

end
Название: Re: Поиск в матрице n*n
Отправлено: Губанов Сергей Юрьевич от Ноябрь 28, 2013, 08:25:21 am
А я для себя вопрос выхода "из середины" закрыл. Если процедура должна быть максимально оптимизирована по быстродействию, и выход "из середины" действиельно даёт выигрыш в производительности, то я его использую без угрызений совести.

Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 28, 2013, 09:31:54 am
Если процедура должна быть максимально оптимизирована по быстродействию, и выход "из середины" действиельно даёт выигрыш в производительности
Вот как раз по этой причине я сомневаюсь в целесообразности запрета break  :)

Не могу склониться полностью на сторону злаbreak только потому, что профит тут получается не алгоритмический, а машиннозависимый. В идеале этим должен оптимизатор заниматься, имхо.

ps Ручная оптимизация - это наверно единственная печенька стороны злаbreak
Название: Re: Поиск в матрице n*n
Отправлено: Geniepro от Ноябрь 28, 2013, 09:51:56 am
ps Ручная оптимизация - это наверно единственная печенька стороны злаbreak
Оберонщики всегда делали упор на ручные оптимизации, утверждая, что компилятор должен быть тупым как пробка.
Поэтому мне непонятен этот их дисонанс с брейком. Как говорится, "или крестик сними, или трусы надень"...
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 28, 2013, 10:05:03 am
Ну можно оптимизировать в рамках структурного программирования, а можно грязно (break).
Думаю, что они как раз грязные оптимизации не признают.  :)
Название: Re: Поиск в матрице n*n
Отправлено: Geniepro от Ноябрь 28, 2013, 10:11:46 am
Ну можно оптимизировать в рамках структурного программирования, а можно грязно (break).
Думаю, что они как раз грязные оптимизации не признают.  :)

Ну и как структурно соптимизировать выход из тройного цикла? Особенно когда каждый такт на счету?
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 28, 2013, 10:12:16 am
Никак
Название: Re: Поиск в матрице n*n
Отправлено: ilovb от Ноябрь 28, 2013, 10:18:10 am
Хотя нужно потестить и посмотреть на сколько структурный вариант проиграет, и проиграет ли вообще.