如何在没有循环的情况下计算APL或J中元素的频率

时间:2011-08-10 08:47:57

标签: j apl

假设我有两个列表,一个是文本t,一个是字符列表c。我想计算每个字符出现在文本中的次数。

使用以下APL代码可以轻松完成此操作。

+⌿t∘.=c

然而它很慢。它取外部产品,然后对每一列求和。

这是一种O(nm)算法,其中n和m是tc的大小。

当然,我可以在APL中编写一个逐字符读t的程序程序,并在O(n + m)中解决这个问题(假设完美哈希)。

有没有办法在没有循环(或条件)的APL中更快地完成这项工作?我也接受J的解决方案。

修改 实际上,我这样做的地方是文本比字符列表短得多(字符是非ascii)。我正在考虑文本的长度为20,字符列表的长度为数千。

如果n小于m ,则有一个简单的优化

w  ← (∪t)∩c
f ←  +⌿t∘.=w
r ← (⍴c)⍴0
r[c⍳w] ← f
r

w只包含t中的字符,因此表大小仅取决于t而不取决于c。该算法在O(n ^ 2 + m log m)下运行。其中m log m是进行交叉运算的时间。

但是,如果某人提供了大量的文本文件,仍然会优先使用子二次算法。

7 个答案:

答案 0 :(得分:8)

NB。使用“key”(/。)副词w / tally(#)动词计数

   #/.~ 'abdaaa'
4 1 1

NB。计算的项目是字符串的结尾。

   ~. 'abdaaa'
abd

NB。所以,如果我们计算目标和字符串

   #/.~ 'abc','abdaaa'
5 2 1 1

NB。我们为每个目标项目额外获得一个。

   countKey2=: 4 : '<:(#x){.#/.~ x,y'

NB。这从xs的每个计数中减去1(&lt; :)。

   6!:2 '''1'' countKey2 10000000$''1234567890'''
0.0451088
   6!:2 '''1'' countKey2 1e7$''1234567890'''
0.0441849
   6!:2 '''1'' countKey2 1e8$''1234567890'''
0.466857

NB。隐性版

   countKey=. [: <: ([: # [) {. [: #/.~ ,

NB。起初看起来要快一点

   6!:2 '''1'' countKey 1e8$''1234567890'''
0.432938

NB。但重复时间10次表明它们是相同的。

   (10) 6!:2 '''1'' countKey 1e8$''1234567890'''
0.43914
   (10) 6!:2 '''1'' countKey2 1e8$''1234567890'''
0.43964

答案 1 :(得分:5)

我认为这个用J编写的例子符合你的要求。字符列表比文本长(但为了方便起见,两者都保持简短。)我没有检查时间,但我的直觉是它会很快。仅仅参考文本中实际出现的字符进行计数,只查看长字符集以关联文本中出现的字符。

   c=: 80{.43}.a.
   t=: 'some {text} to examine'

   RawIndicies=: c i. ~.t
   Mask=: RawIndicies ~: #c
   Indicies=: Mask # RawIndicies
   Tallies=: Mask # #/.~ t
   Result=: Tallies Indicies} (#c)$0

   4 20 $ Result
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 4 0
0 0 1 0 0 0 2 1 2 0 0 0 1 3 0 0 0 2 0 0
   4 20 $ c
+,-./0123456789:;<=>
?@ABCDEFGHIJKLMNOPQR
STUVWXYZ[\]^_`abcdef
ghijklmnopqrstuvwxyz

答案 2 :(得分:4)

Dyalog v14引入了关键操作符():

      {⍺,⍴⍵}⌸'abcracadabra'
a 5
b 2
c 2
r 2
d 1

操作数函数将的字母和该字母(索引的矢量)的出现设为

答案 3 :(得分:0)

J:

中的“蛮力”
count =: (i.~~.) ({,&0) (]+/"1@:=)

用法:

   'abc' count 'abdaaa'
4 1 0

不确定它是如何在内部实现的,但这里是不同输入大小的时间:

   6!:2 '''abcdefg'' count 100000$''abdaaaerbfqeiurbouebjkvwek''' NB: run time for #t = 100000 
0.00803909
   6!:2 '''abcdefg'' count 1000000$''abdaaaerbfqeiurbouebjkvwek'''
0.0845451
   6!:2 '''abcdefg'' count 10000000$''abdaaaerbfqeiurbouebjkvwek''' NB: and for #t = 10^7
0.862423

我们不会在'自我分类'之前过滤输入日期,所以:

   6!:2 '''1'' count 10000000$''1'''
0.244975
   6!:2 '''1'' count 10000000$''1234567890'''
0.673034
   6!:2 '''1234567890'' count 10000000$''1234567890'''
0.673864

答案 4 :(得分:0)

我在APL中的实施(NARS2000):

(∪w),[0.5]∪⍦w←t∩c

示例:

      c←'abcdefg'
      t←'abdaaaerbfqeiurbouebjkvwek'
      (∪w),[0.5]∪⍦w←t∩c
a b d e f
4 4 1 4 1

注意:仅显示t中存在的那些字符

答案 5 :(得分:0)

如其他答案中所述,关键操作员直接执行此操作。然而,解决这个问题的经典APL方法仍然值得了解。

经典的解决方案是&#34;排序,转换和比较&#34;:

      c←'missippi'
      t←'abcdefghijklmnopqrstuvwxyz'
      g←⍋c
      g
1 4 7 0 5 6 2 3
      s←c[g]
      s
iiimppss
      b←s≠¯1⌽s
      b
1 0 0 1 1 0 1 0
      n←b/⍳⍴b
      n
0 3 4 6
      k←(1↓n,⍴b)-n
      k
3 1 2 2
      u←b/s
      u
imps

最后的答案:

       z←(⍴t)⍴0
       z
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
       z[t⍳u]←k
       z
0 0 0 0 0 0 0 0 3 0 0 0 1 0 0 2 0 0 2 0 0 0 0 0 0 0

此代码不在我的头顶,尚未准备好投入生产。必须寻找空案例 - 布尔移位可能不适合所有情况....

答案 6 :(得分:0)

我最初的想法是 Find 运算符的情况:

T←'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
C←'MISSISSIPPI'
X←+/¨T⍷¨⊂C

使用的字符是:

       (×X)/T
IMPS

他们各自的频率是:

       X~0
4 1 2 4

我只运行玩具箱,所以我不知道它的性能是什么,但我的直觉告诉我外层产品应该更便宜。 有什么想法吗?