具有重复索引的数组的总和

Sum of arrays with repeated indices

如何通过索引将一个数字数组添加到另一个数组?特别是对于重复索引。那样

   x
1 2 3 4
  idx
0 1 0
   y
5 6 7
   ] x add idx;y NB. (1 + 5 + 7) , (2 + 6) , 3 , 4
13 8 3 4

所有名词 (x, idx, y) 可以是数百万项,我需要快速 'add' 动词。

更新

解决方案(感谢 Dan Bron):

   cumIdx =: 1 : 0
   :
   'i z' =. y
   n =. ~. i
   x n}~ (n{x) + i u//. z
   )
   (1 2 3 4) + cumIdx (0 1 0);(5 6 7)
13 8 3 4

目前,在“搞定”模式下的简短回答:

data   =.  1 2 3 4
idx    =.  0 1 0
updat  =.  5 6 7

cumIdx =:  adverb define
:
  n =. ~. m
  y n}~ (n{y) + m +//. x
)

  updat idx cumIdx data  NB. 13 8 3 4

简述:

  1. 首先对索引数组具有相同值的更新数组(在您的 post、y¹ 中)进行分组,然后取每组的总和
    1. 使用 the adverb key (/.) 以总和 (+/) 作为其动词参数来完成此操作,派生一个二元动词,其参数为左侧的 idx 和右边的更新数组(你的y,我的updat)。
  2. 获取索引数组的核心 (~.)
  3. Select 来自您的值数组的这些(唯一)索引(您的 x,我的 data
    1. 根据定义,这将与我们在 (1.) 中计算的累积和具有相同的长度
  4. 将这些添加到累计总和
  5. 现在您有了对数据的最终更新; updatidx 具有相同的长度,因此您只需使用 } 将它们合并到您的值数组中,就像您在代码中所做的那样

由于我们保持更新数组较小(永远不会大于其原始长度),这在较大的输入上应该有不错的性能,尽管我没有 运行 任何测试。唯一的性能缺点是 idx 的 nub 的双重计算(一次显式使用 ~.,一次隐式使用 /.),尽管由于您的值是整数,这应该相对便宜;就性能而言,这是 J 的强项之一。


¹ 我意识到重命名数组会使这个答案比需要的更冗长。但是,由于您将主数据命名为 x 而不是 y(这是约定),如果我只是保留您的命名约定,那么当我调用 cumIdx 时,定义内的名词与定义外的名词具有相反的含义,我认为这会引起更大的混淆。因此,最好将“原始数据”放在右边(y),将“control data”放在左边(x)。

您还可以考虑将特殊名称 xyuvmn 的使用限制在它们已经通过调用显式定义被隐式定义;绝对不要改变他们的名字类别。

此方法也使用密钥 (/.),但其方法更加简单。 它可能会使用更多 space,特别是对于比 Dan Bron 的大更新。

   addByIdx=: {{ (m , i.@# y) +//. x,y }}
   updat idx addByIdx data
13 8 3 4