OberonCore
https://forum.oberoncore.ru/

Еще один этюд: пошаговый обход многомерного массива
https://forum.oberoncore.ru/viewtopic.php?f=27&t=2981
Страница 1 из 2

Автор:  Peter Almazov [ Вторник, 09 Ноябрь, 2010 11:07 ]
Заголовок сообщения:  Еще один этюд: пошаговый обход многомерного массива

Случайно натолкнулся на этот кусок – резанул глаз выход из середины цикла for. После прочтения комментария, понял, что задача хорошо подходит в качестве этюда при обучении – простая, но нужно подумать.
Речь идет о пошаговом обходе многомерного массива. Программа выполняет один шаг – увеличивает текущую комбинацию индексов, начиная с последнего. Если отобразить этот массив на одномерный, то все, что нужно - увеличить его индекс на 1, пока не зайдем за верхнюю границу, тогда _complete = true. Если потом обратно пересчитать одномерный индекс в комбинацию многомерных, то получим ответ.
Границы размерностей задаются функциями array.GetUpperBound(номер размерности)и GetLowerBound, последняя не обязательно равна нулю. Но это не принципиально.
Вот как решили это в Microsoft (C#).
Код:
private void IncArray() {
  // This method advances us to the next valid array index,
  // handling all the multiple dimension & bounds correctly.
  // Think of it like an odometer in your car - we start with
  // the last digit, increment it, and check for rollover.  If
  // it rolls over, we set all digits to the right and including
  // the current to the appropriate lower bound.  Do these overflow
  // checks for each dimension, and if the most significant digit
  // has rolled over it's upper bound, we're done.
  //
  int rank = array.Rank;
  _indices[rank - 1]++;
  for (int dim = rank - 1; dim >= 0; dim--) {
    if (_indices[dim] > array.GetUpperBound(dim)) {
      if (dim == 0) {
        _complete = true;
        break;
      }
      for (int j = dim; j < rank; j++)
        _indices[j] = array.GetLowerBound(j);
      _indices[dim - 1]++;
    }
  }
}
Предлагаю дать свои решения. Главное, конечно, описать ход решения или инструмент. Кстати, подозреваю, что решение красиво выглядит на ФЯ, было бы интересно его посмотреть.
Свой вариант прилагаю в запароленном виде.

Вложения:
IncArray.rar [1.09 КБ]
Скачиваний: 495

Автор:  Alexey Veselovsky [ Вторник, 09 Ноябрь, 2010 12:16 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

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

Автор:  Александр Ильин [ Вторник, 09 Ноябрь, 2010 13:00 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

А мне глаз резануло, что используется вложенный цикл.

Автор:  Евгений Темиргалеев [ Вторник, 09 Ноябрь, 2010 13:01 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Схема (пред/пост усл. стёр, выписаны в "пробе"):
Код:
i := 0; WHILE i < n DO x[i] := GetLowerBound(a, i); INC(i) END;
i := n - 1;
WHILE x[n - 1] <= GetUpperBound(a, n - 1) DO
   (* обработать a[x[0], ...., x[n - 1]] *)
   INC(x[n - 1])
ELSIF (i >= 0) & (x[i] >= GetUpperBound(a, i)) THEN
   DEC(i)
ELSIF i >= 0 THEN
   INC(x[i]);
   REPEAT INC(i); x[i] := GetLowerBound(a, i) UNTIL i = n - 1
END
Дописал: схема --- пошагового обхода многомерного массива (а не процедуры IncArray)
Проба:
Код:
StdCoder.Decode ..,, ..fy....3Qw7uP5PRPPNR9Rbf9b8R79FTvMf1GomCrlAy2xhX,Cb2x
 hXhC6FU1xhiZiVBhihgmRiioedhgrZcZRiXFfaqmSrtuGfa4700zdGrr8rmCLLCJuyKtYcZRiX
 7.2.s,6AT.,6.5Qw7uP51QCPuP7PNN9F9vQAy1xB.gdj,UBxhYhAbf9P0G2sIdvPZntgcghghZ
 cZRC8T0E.Ezu.H.hf12.,U08J99SdfJHPNjvQCJuGKfaqmY6MwdONl1QCh0708T,U..w.2YC.,
 sUGpmWbBxhYhAbndMHT9NY6Mw.sQq2Y6cwB.0.J71w,IYAE.0.4kl5.86.QC18RdfQHfMf9R9v
 Q7ONb17.,.D,,6.I16.M.,U.IZ.2XkD.1.VQ.6.301cUZT1E.s7E.c46.,.16.c8U.E.076i1U
 0Ikmj,6.2A2.I16.M.2.J,U.Yr0.B.1cQ,U0o.AU6B.8k,k.4q.kCk.Sp.E3Ua1,E.0E2PP,2e
 HJ.3Qw7ONhvETPPPPMR9N9fQbf9b8RO3U.Ay2hgq,.RdJ.0EtD.0E.66,E.0.O.2.fU,23A.6F
 rO.0pd8...oZHlWuKmOpoK46.X5.ELKIrGKf.2Um10..IU.U.AU.4.0k,2.myzzzzB1AUC.H,u
 1Im.k.y,g,6E2U.2A0kzXGJ0GoGLu0LRymLOqr8ruqKLyKlKKtyKrCqr8rmuGtKrLOroKaoxhk
 BhXpZk3hkxbahbmwaaYixIat2bloYnZiVJiohbk2YeAZBgdDZcJZd33YEJidpi3phphh7phYBh
 XhgnRbBgV7AdB3eDJeI3Y7phUIbx2YdJalQitRi7phg2YAxhbRbBAVBAVIBfEhcBAV7Yctph,J
 imBaq2YxgV7AV7AcGJe,BfUwd4hV7AV7.............................EEaIbGpWSoW8p
 Rqk2qUKBcGhV7AVVJbU2eDBdCZe3JeUYeD3Y2BjiB6sCPM0H6ON76NfC,NEZeI1WloaUwd432s
 F.RfC,NGR0k4aEc8pbCoWGoe8pW0mXKKuKJs0rm8LVyquuKm0GIaKR0mYu2M8p76H0sCaEVKoX
 aIbqk2akUCpdKIdGJIaKEaIb0mx0HLuGrqmMqLK0GN0nIin4ak28pWGpe8Jb0Goipoqp4akWuI
 W0mXKKu.UvgV7g,V0.6HTvR9fQ3uP66.Ud.Uv........NvKHPL...i1..HePVOMHfQ,78hOEZ
 86l99,NSp76H0M8rN1HcE.aIrumYuKuWGwamR0mYuKLa2arIin4ak242EzaIruGmyKrKKEyId0
 GI0HEmnS0GwaGEOGEWGw0GSqXtBZg2YkAZBAV3V7phEVvgV7g,V0.NuPDPGR9N,lYuIEWLR0mU
 .T0.UKB6HMOp76.i130ko0GRqHE0nRqk2akfWoYmoW0mo0GS0GaKIbWGwaGEGobqk2ak2morSK
 LaIrGbsRfdhfdQbBU7pd13ZdBZBAV7gcCtCPM0m2mIrqk2KIb2YAxBsCqk2.U2xheQbBAV7o8q
 .HMON76JfC,NG.k2WLR0Gcy2I8MEZeI.kX.30kYuaD3iZphvgV7AVi3Yu662Y7phEVcYhPphRZ
 ZU2hPphRBZvg,S3MGRf971Gobk2aIbcPHtCP.uKc44Ug.a0g6i142Er0GTqHE4HK0GMQbBko00
 uqR0mfWoYm2H1XdB,7FT86FvKHHEenSAav2Y7pd13Zd769eH7uCcH9uJFNMN76FnMKnimGEW4d
 NLN76FXngfg6OIaRZZUUlgfg222aRZZUkQqJK00lNLN76waRZZUEPqJK00fNLN76YaRZZUkNqJ
 K00ZNLN76AaRZZUEMqpIin468J76PN9PN9P,..66JN8PM0HcH6SNFramRkoUkkfW2.R967uHWr
 ha4sF91.2ZdBZv2Y7,..ohUgZUAavgV76HTuHV86HeF,7SrePPNAv86tND,tF9ne.Ui3YhkI0G
 eWoWuo4aU7YdjNG2ZsB3PU7AdCFwiJr0mK0WRBZBA,98HbOGB86FNO,dDv76VN8,d7sKcDvlX.
 .ABH76d0EWKoVWmoam4.MO,dD2aUYe6h6kY.HnI.ZGcKoUGJEaIbCYdtCWrh66pV5Fa..0meuI
 eaIa0mo0mS0GrqmMqk2a.KIEKIgaIe0mWuIW0mWsC68.e0Y7uGaQbBA,N0H0HP8rFayqnuGa.M
 G6SU,,ABvlK4HK0GP0nIi1eKEenS2avg,S3cO,7D,dPqWm2Y2x71uIbOF6SreOv86v76DWJ,.J
 P8N76h,,,eqI0mWu2ME.oBA4,ND,,.Ui,HlJ0mMmGEO1PM0667uPrN1PMFR8F0Jta4MG.RN1Pc
 .VeQHfR..7uPPM0VN0bN1HcAHsAPM0XN0dN1H6BHMBPM03M1PM1R96xND4n4qko0GRUU.ABoB7
 Ws,sFY7.MMN76HP8rlYko2Y3,PMO,,RP9in4SJYa2WrhuqK.S2f0.ErqmMaGEGob68JFTGsMGM
 UGMMGsMGMTGMVGMMGMVGMaGME4qhWrh0Xg2Yic9R79,7SrePML,d8HV7p7WbiVdgVB2Ze2YsR9
 66v,...Q30GJaW3ZdHB7W0xVkA3UdFTsF..koA3.7OFk4q.....cP.qEH0GIqk2Wmo0GS24Pkb
 8p4aEIaayEM.Uw2Y5hA..AB,d7,,JXyg5D0..eqImWehbdRZloZiohhM8PM0HN1,d8H,koUk22
 U7p7MOa0W0RP9X76x76V76JN8PM0ZOF.MOiXsVUI5S2N0.koMJG3Uil4KIbgVB2Ze68HnSg3H7
 6B,kSw6K3.UeB3g5.ZN8,d7,,R1...oBaGEi0H76JN83QwdONQcjphoJijZhghgmRiiQ8CIu8L
 qGomCrl0ksH3..RtETfPd16F9vQ59.C244.IC...Qii..70,cw7U.2.Y02.A,2U..U,Iklb8Ie
 pZhZJinpZHFdKLq6F6.XDJ.QiiIepZhZ7F6.Zz.E.ses,sc6.,k,,UnpZHldGrwmqmGomCb.AS
 .c9Ajg,0EtT.,E..W.e30E.cUX5.umUG5.70,E0E...7,,M.,.,.,tcp00kMDy.wnjl.k.E.0.
 3gwP.0..I16.M.EJ.6.VQ.E..YVsH4EKithQVs9E3Qw7uPghZ,5uP..Y0,6.,..2,2.M00.lwG
 bHESmF3kwL,,AzJEAyIo2oe2H.BN,...
 --- end of encoding ---

Автор:  Александр Ильин [ Вторник, 09 Ноябрь, 2010 13:03 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Евгений Темиргалеев писал(а):
Схема (пред/пост усл. стёр, выписаны в "пробе")
Опять вложенный цикл. Да что ж такое-то?

UPD: Понял, здесь вложенный цикл оправдан. Правда, задача решена несколько иная.

Автор:  Евгений Темиргалеев [ Вторник, 09 Ноябрь, 2010 13:10 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Александр Ильин писал(а):
Евгений Темиргалеев писал(а):
Схема (пред/пост усл. стёр, выписаны в "пробе")
Опять вложенный цикл. Да что ж такое-то?
Для Вас задачка по исключению :)

Автор:  Peter Almazov [ Вторник, 09 Ноябрь, 2010 13:21 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Alexey Veselovsky писал(а):
А можно уточнить откуда что берется и что должно быть на выходе? Потому как функция без аргументов и без возвращаемого значения, которая при этом тем не менее откуда-то с потолка берет какие-то массивы, меня как-то смущает.
На входе в массиве _indices лежит текущая комбинация индексов (вначале все нижние границы), на выходе там же комбинация "увеличенная на 1". Булева переменная _complete - по сути выходная, "истина" если происходит выход за границы (переполнение одометра).

Автор:  Александр Ильин [ Вторник, 09 Ноябрь, 2010 13:32 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Евгений Темиргалеев писал(а):
Для Вас задачка по исключению :)
Сейчас, вот только полный тест прогоню...

Автор:  Александр Ильин [ Вторник, 09 Ноябрь, 2010 13:59 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Код:
MODULE PrivOdometer;

IMPORT Log;

CONST
   arrayRank = 6;
   maxTestValue = 999999; (* arrayRank девяток, для теста *)
VAR
   indices, upperBound, lowerBound: ARRAY arrayRank OF INTEGER;
   completed: BOOLEAN;

   PROCEDURE IncArray;
   VAR dim: INTEGER;
   BEGIN
      dim := arrayRank - 1;
      INC(indices[dim]);
      WHILE (dim > 0) & (indices[dim] > upperBound[dim]) DO
         DEC(dim);
         INC(indices[dim])
      END;
      IF (dim = 0) & (indices[dim] > upperBound[dim]) THEN
         completed := TRUE
      ELSE
         INC(dim);
         WHILE dim < arrayRank DO
            indices[dim] := lowerBound[dim];
            INC(dim)
         END
      END
   END IncArray;

   PROCEDURE LogIndices;
   VAR i: INTEGER;
   BEGIN
      FOR i := 0 TO arrayRank - 1 DO
         Log.Char(CHR(ORD('0') + indices[i]));
      END;
   END LogIndices;

   PROCEDURE DoTest (value: INTEGER; log: BOOLEAN);
   VAR i, v: INTEGER;
   BEGIN
      ASSERT(value >= 0, 20);
      ASSERT(~completed, 21);
      v := value;
      (* 'v' to 'indices' *)
      FOR i := arrayRank - 1 TO 0 BY -1 DO
         indices[i] := v MOD 10;
         v := v DIV 10;
      END;
      IF log THEN
         LogIndices;
         Log.String(" + 1 = ");
      END;
      IncArray;
      IF log THEN
         LogIndices;
         Log.Ln;
      END;
      (* 'indices' to 'v' *)
      v := 0;
      FOR i := 0 TO arrayRank - 1 DO
         v := 10 * v + indices[i];
      END;
      ASSERT((v = value + 1) OR (value = maxTestValue) & completed, 60);
   END DoTest;

   PROCEDURE Test*;
   VAR i: INTEGER;
   BEGIN
      completed := FALSE;
      FOR i := 0 TO arrayRank - 1 DO
         upperBound[i] := 9;
         lowerBound[i] := 0;
      END;
      FOR i := 0 TO maxTestValue DO
         DoTest (i, FALSE)
      END;
   END Test;

END ^Q PrivOdometer.Test

Автор:  Александр Ильин [ Вторник, 09 Ноябрь, 2010 14:41 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Кстати, майкрософтовский вариант у меня выполняется медленнее:
Код:
   PROCEDURE IncArray;
   VAR dim, j, rank: INTEGER;
   BEGIN
      rank := arrayRank;
      INC(indices[rank - 1]);
      dim := rank - 1;
      WHILE dim >= 0 DO
         IF indices[dim] > upperBound[dim] THEN
            IF dim = 0 THEN
               completed := TRUE;
               RETURN
            END;
            j := dim;
            WHILE j < rank DO
               indices[j] := lowerBound[j];
               INC(j)
            END;
            INC(indices[dim - 1])
         END;
         DEC(dim)
      END
   END IncArray;

Автор:  Peter Almazov [ Вторник, 09 Ноябрь, 2010 14:54 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Да забейте Вы на него. Представьте себе счетчик, у которого циферки справа бегут быстро, а чем левее, тем медленнее. И запрограммируйте один такт его работы.

Автор:  Александр Ильин [ Вторник, 09 Ноябрь, 2010 14:57 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Peter Almazov писал(а):
Да забейте Вы на него. Представьте себе счетчик, у которого цифорки справа бегут быстро, а чем левее, тем медленнее. И запрограммируйте один такт его работы.
Ну, как бы уже сделал, если это вы мне.

Автор:  Сергей Губанов [ Вторник, 09 Ноябрь, 2010 15:54 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Код:
   PROCEDURE Inc;
      PROCEDURE inc (i: INTEGER);
      BEGIN
         INC(indices[i]);
         IF indices[i] > upperBound[i] THEN
            indices[i] := lowerBound[i];
            IF i > 0 THEN
               inc(i-1)
            ELSE
               completed := TRUE
            END
         END
      END inc;
   BEGIN
      inc(rank-1)
   END Inc;

Автор:  Valery Solovey [ Вторник, 09 Ноябрь, 2010 15:57 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Ваша задача такая?
Имеется последовательность чисел, зажатых ограничениями. Требуется к крайнему правому числу добавить единицу и, возможно, выполнить дополнительные преобразования для удовлетворения ограничений. Если я правильно понимаю ограничения, то
1. у каждого числа имеется определённый диапазон, из которого он не должен выходить
2. если появляется угроза выхода из диапазона, то данное число принимает минимально возможное значение, а к самому правому числу перед текущим прибавляется единица.
Вот мой код (прошу прощения, я его не проверял, поэтому за корректность не совсем ручаюсь).
Код:
i := rank - 1;
WHILE indices[i] = array.GetUpperBound(i) AND i >= 0 DO
   indices[i] := array.GetLowerBound(i);
   i := i - 1
END;

overflow := i < 0;

IF ~overflow THEN
   indices[i] = indices[i] + 1
END


Но там прибавляется не единица.

Код:
i := rank - 1;
WHILE indices[i] + rank - i > array.GetUpperBound(i) AND i >= 0 DO
   indices[i] := array.GetLowerBound(i);
   i := i - 1
END;

overflow := i < 0;

IF ~overflow THEN
   indices[i] = indices[i] + rank - i
END

Автор:  Сергей Губанов [ Вторник, 09 Ноябрь, 2010 16:08 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Valery Solovey писал(а):
Вот мой код (прошу прощения, я его не проверял, поэтому за корректность не совсем ручаюсь).
Только сначала надо проверять i>=0, а потом индексировать, а так отличный код.

Код:
   PROCEDURE Inc;
      VAR i: INTEGER;
   BEGIN
      i := rank -1;
      WHILE (i >= 0) & (indices[i] = upperBound[i]) DO
         indices[i] := lowerBound[i];
         DEC(i)
      END;
      IF i >= 0 THEN
         INC(indices[i])
      ELSE
         completed := TRUE
      END
   END Inc;

Автор:  Александр Ильин [ Вторник, 09 Ноябрь, 2010 16:21 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Valery Solovey писал(а):
Ваша задача такая?
Отлично! И работает ещё чуть быстрее.

Автор:  Valery Solovey [ Вторник, 09 Ноябрь, 2010 16:43 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

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

Автор:  Alexey Veselovsky [ Вторник, 09 Ноябрь, 2010 18:08 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Вот навелосипедил:
Код:
module Indexes (Index, incArray, test) where

data Index = Index { dwnBound :: Int, value :: Int, upBound :: Int} deriving Show

add n idx@(Index d v u) = ( (v'+n) `div` (u'+1),
                            idx {value = (v'+n) `mod` (u'+1) + d} )
    where d' = 0
          v' = v-d
          u' = u-d

incArray idxs = idxs'
    where (_,idxs') = foldl (\(a,ix) i -> case (add a i) of (a',i') -> (a',i':ix))
                            (1,[])
                            (reverse idxs)   

test = incArray [Index 0 8 9, Index 0 9 9]


Пойду теперь гляну как это сделано в стандартной либе :-)

Автор:  Alexey Veselovsky [ Вторник, 09 Ноябрь, 2010 19:22 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Вообще, на самом деле, для обхода многомерного массива эта функция совершенно не нужна.
Обходится многомерный массив как-то так:
Код:
process f acc arr = foldl f acc (elems arr)

Автор:  Valery Solovey [ Вторник, 09 Ноябрь, 2010 19:25 ]
Заголовок сообщения:  Re: Еще один этюд: пошаговый обход многомерного массива

Код:
-module(arr).

-export([next/1]).

next([]) -> io:format("~p~n", [overflow]), [];
next([{Min, Cur, Max}| Tail]) -> % список заранее развёрнут (reverse/1).
   if
      Cur + 1 > Max -> [{Min, Min, Max} | next(Tail)];
      Cur + 1 =< Max -> [{Min, Cur + 1, Max} | Tail]
   end.

Страница 1 из 2 Часовой пояс: UTC + 3 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/