Algorithm APL中X表示的累积最大值

Algorithm APL中X表示的累积最大值,algorithm,sorting,apl,Algorithm,Sorting,Apl,表中的第三项称为“累积最大值”(⌈) 由X“表示的Y的子向量,其中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 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

表中的第三项称为“累积最大值”(⌈) 由X“表示的Y的子向量,其中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
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数组中的起始值或任何1值开始,Y中所有对应数字的累积最大值都会被找到,直到X中找到另一个1。在给定的示例中,X将数组分成两个相等的部分,每个部分4个数字。在第一部分中,9是最大值,直到遇到78为止,在第二部分中第50部分是最大值,直到遇到第69部分

这很容易理解,我可以盲目地使用它,但我想了解它是如何工作的,因为APL习语本质上是由运算符和函数组成的算法。要理解APL,很重要的一点是理解大师们是如何将它们编织成如此紧凑而优雅的代码行的


我发现这个特别的习语特别难以理解,因为索引嵌套了两层。所以我的问题是,是什么让这个习语滴答作响?

这个习语可以分解成更小的习语,最重要的是,它包含了FinnAPL库中的习语11,名为:

升级(⍋) 用于对X指示的Y的子向量进行排序

使用问题中给出的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
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表示需要Y(2)的第四位在第一个位置;3表示第二个位置的第三位(3);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)应用于A的X
1 2 1
的值作为左压缩和右压缩的组合。与A 1对齐的A的所有值都向左移动,与A 2对齐的值向右移动。当然,如果X有更多的1,它将按照A的值指示的顺序压缩和定位压缩包SCA结果。例如,如果X的SCA类似于
3 3 2 2 1 1
,则会得到与1对应的4位数字,然后是与2对应的3位数字,最后是与3对应的2位数字

您可能已经注意到,我跳过了显示升级效果的步骤

(+\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实现的。正如我在另一篇文章中所讨论的,它有效地充当了秩。在那篇文章中,我还谈到了秩和索引本质上是同一硬币的两面,你可以使用grade up在两者之间切换。因此,这就是这里发生的事情: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
这就剩下最后一步来完成索引。在执行累积最大值操作后,向量值仍然表示秩,因此需要将它们转换回索引值。为此,使用索引运算符。它获取右参数中的值,并返回左参数中的位置:

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在左参数中位于第二位,因此结果显示右参数中每4个都有一个2。索引已完成,因此将其应用于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

祝你有愉快的一天。

我非常喜欢你对各种APL习语的讲解。这些习语非常简洁,通过它们可以更深入地理解诸如grade up.和down之类的原语。也就是说,应该注意的是,具有嵌套数组的现代APL可以直接、清晰地解决许多旧习语。例如,只需对每个分区向量进行最大扫描即可:∊⌈\¨X⊂Y请注意,分区原语和登记原语根据APL实现的不同而不同,因此此表达式可能需要调整。@PaulMansour-很高兴听到它!我发现它非常具有挑战性,但这也是一种愉快的学习体验。另外请注意,Dyalog的下一个版本有一个名为“key”的新运算符,受Roger Hui和J语言的启发,我认为这将解决这个特殊问题,而无需使用每个运算符或将结果合并回一个简单的向量。