Совокупные максимумы, обозначенные буквой X в APL

Третий элемент в библиотеке FinnAPL называется "Кумулятивные максимумы (⌈) субвекторов Y, обозначенных X", где X - двоичный вектор, а Y - вектор чисел. Вот пример его использования:

X←1 0 0 0 1 0 0 0
Y←9 78 3 2 50 7 69 22
Y[A⍳⌈\A←⍋A[⍋(+\X)[A←⍋Y]]]       ⍝ output 9 78 78 78 50 50 69 69

Вы можете видеть, что, начиная с начала или любого значения 1 в массиве X, кумулавный максимум находится для всех соответствующих цифр в Y, пока в X не будет найдена еще одна цифра 1. В приведенном примере X делит массив на два равные части по 4 номера в каждой. В первой части 9 - максимумы до 78, а во второй части 50 - максимумы до 69.

Это достаточно легко понять, и я мог бы слепо использовать его как есть, но я хотел бы понять, как он работает, потому что идиомы APL - это, по сути, алгоритмы, состоящие из операторов и функций. Чтобы хорошо понимать APL, важно понимать, как мастера смогли объединить все это в такие компактные и элегантные строки кода.

Я нахожу эту особую идиому особенно трудной для понимания из-за глубины вложенности двухуровневой индексации. Итак, мой вопрос, что делает эту идиому галочкой?

2 ответа

Эта идиома может быть разбита на более мелкие идиомы, и что наиболее важно, она содержит идиому № 11 из библиотеки FinnAPL под названием:

Повышение (⍋) для сортировки субвекторов Y, обозначенных X

Используя те же значения для X и Y, которые приведены в вопросе, вот пример его использования:

X←1 0 0 0 1 0 0 0
Y←9 78 3 2 50 7 69 22
A[⍋(+\X)[A←⍋Y]]             ⍝ output 4 3 1 2 6 8 5 7

Как и прежде, X делит вектор на две половины, и выходные данные указывают, для каждой позиции, какая цифра Y необходима для сортировки каждой из половин. Итак, 4 в выводе говорит, что ему нужна 4-ая цифра Y (2) в 1-й позиции; 3 обозначает 3-ю цифру (3) во 2-й позиции; 1 обозначает первую цифру (9) в третьей позиции; и т.д. Таким образом, если мы применим это индексирование к Y, мы получим:

Y[A[⍋(+\X)[A←⍋Y]]]          ⍝ output 2 3 9 78 7 22 50 69

Чтобы понять индексацию в рамках этой идиомы повышения квалификации, рассмотрим, что происходит со следующим:

(+\X)[A←⍋Y]                 ⍝ Sorted Cumulative Addition

Разбивая это шаг за шагом:

A←⍋Y                        ⍝ 4 3 6 1 8 5 7 2
+\X                         ⍝ 1 1 1 1 2 2 2 2
(+\X)[A←⍋Y]                 ⍝ 1 1 2 1 2 2 2 1 SCA
A[⍋(+\X)[A←⍋Y]]             ⍝ 4 3 1 2 6 8 5 7

Вы можете видеть, что отсортированное совокупное сложение (SCA) X 1 1 2 1 2 2 2 1 применяется к A действует как комбинация компрессии влево и компрессии вправо. Все значения A, совпадающие с 1, сдвигаются влево, а значения, совпадающие с 2, сдвигаются вправо. Конечно, если бы X имел больше 1 с, он бы сжимал и определял местоположение сжатых пакетов в порядке, указанном значениями результата SCA. Например, если SCA X были похожи 3 3 2 1 2 2 1 1 1В итоге вы получите 4 цифры, соответствующие 1 секундам, затем 3 цифры, соответствующие 2 секундам, и, наконец, 2 цифры, соответствующие 3 секундам.

Возможно, вы заметили, что я пропустил шаг, который покажет эффект повышения :

(+\X)[A←⍋Y]                 ⍝ 1 1 2 1 2 2 2 1 SCA
⍋(+\X)[A←⍋Y]                ⍝ 1 2 4 8 3 5 6 7 Grade up
A[⍋(+\X)[A←⍋Y]]             ⍝ 4 3 1 2 6 8 5 7

Эффект сжатия и перестановки не достигается одной только SCA. Это эффективно действует как звание, как я обсуждал в другом посте. Также в этом посте я говорил о том, что ранг и индекс по сути являются двумя сторонами одной медали, и вы можете использовать оценку вверх для переключения между ними. Следовательно, это то, что происходит здесь: SCA преобразуется в индекс для применения к A, и эффект - это сортированные по вектору субвекторы, как указано X.

От отсортированных субвекторов к совокупным максимумам

Как уже было описано, результатом сортировки субвекторов является индекс, который при применении к Y сжимает данные в пакеты и упорядочивает эти пакеты в соответствии с X. Дело в том, что это индекс, и снова применяется повышение класса, который конвертирует индексы в ранги:

⍋A[⍋(+\X)[A←⍋Y]]            ⍝ 3 4 2 1 7 5 8 6

Вопрос здесь в том, почему? Что ж, следующим шагом является применение кумулятивных максимумов, и это действительно имеет смысл, только если оно применяется к значениям для ранга, которые представляют относительную величину в каждом пакете. Глядя на значения, вы можете видеть, что 4 - это максимумы для первой группы из 4, а 8 - для второй группы. Эти значения соответствуют входным значениям 78 и 69, что мы и хотим. Не имеет смысла (по крайней мере, в этом случае) применять максимумы к значениям индекса, которые представляют позицию, поэтому необходимо преобразование в ранг. Применение кумулятивных максимумов дает:

⌈\A←⍋A[⍋(+\X)[A←⍋Y]]        ⍝ 3 4 4 4 7 7 8 8

Это оставляет последний шаг для завершения индекса. После выполнения операции кумулятивных максимумов векторные значения по-прежнему представляют ранг, поэтому их необходимо преобразовать обратно в значения индекса. Для этого используется оператор index-of. Он принимает значение в правом аргументе и возвращает их позицию, найденную в левом аргументе:

A⍳⌈\A←⍋A[⍋(+\X)[A←⍋Y]]      ⍝ 1 2 2 2 5 5 7 7

Чтобы было легче увидеть:

3 4 2 1 7 5 8 6             left argument
3 4 4 4 7 7 8 8             right argument
1 2 2 2 5 5 7 7             result

4 находится во 2-й позиции в левом аргументе, поэтому результат показывает 2 для каждых 4 в правом аргументе. Индекс завершен, поэтому, применив его к Y, мы получим ожидаемый результат:

Y[A⍳⌈\A←⍋A[⍋(+\X)[A←⍋Y]]]    ⍝ 9 78 78 78 50 50 69 69

Моя реализация:

      X←1 0 0 0 1 0 0 0
      Y←9 78 3 2 50 7 69 22

      ¯1+X/⍳⍴X ⍝ position
0 4 
      (,¨¯1+X/⍳⍴X)↓¨⊂Y
 9 78 3 2 50 7 69 22  50 7 69 22

      (1↓(X,1)/⍳⍴X,1)-X/⍳⍴X ⍝ length
4 4 
      (,¨(1↓(X,1)/⍳⍴X,1)-X/⍳⍴X)↑¨(,¨¯1+X/⍳⍴X)↓¨⊂Y
 9 78 3 2  50 7 69 22

      ⌈\¨(,¨(1↓(X,1)/⍳⍴X,1)-X/⍳⍴X)↑¨(,¨¯1+X/⍳⍴X)↓¨⊂Y
 9 78 78 78  50 50 69 69
      ∊⌈\¨(,¨(1↓(X,1)/⍳⍴X,1)-X/⍳⍴X)↑¨(,¨¯1+X/⍳⍴X)↓¨⊂Y
9 78 78 78 50 50 69 69

Хорошего дня.

Другие вопросы по тегам