如何在J中以惯用方式生成Rowland素数序列?

时间:2010-06-10 08:53:20

标签: tacit-programming j

如果你不熟悉Rowland素数序列,你可以找到它here。我在J中创建了一个丑陋的,程序性的monadic动词来生成这个序列中的第一个 n 术语,如下所示:

rowland =: monad define
    result =. 0 $ 0
    t =. 1 $ 7
    while. (# result) < y do.
        a =. {: t
        n =. 1 + # t
        t =. t , a + n +. a
        d =. | -/ _2 {. t
        if. d > 1 do.
            result =. ~. result , d
        end.
    end.
    result
)

这非常有效,它确实在序列中生成了第一个 n 项。 (通过 n 术语,我的意思是第一个 n 不同素数。)

以下是rowland 20的输出:

5 3 11 23 47 101 7 13 233 467 941 1889 3779 7559 15131 53 30323 60647 121403 242807

我的问题是,我怎么能用更惯用的J来写这个?我没有线索,虽然我写了以下函数来查找列表中每个连续数字之间的差异数字,这是必需步骤之一。虽然它也可能被一个比我更有经验的J程序员重构,但是:

diffs =: monad : '}: (|@-/@(2&{.) , $:@}.) ^: (1 < #) y'

2 个答案:

答案 0 :(得分:3)

虽然我已经将estanford的答案标记为正确的答案,但自从我提出这个问题以来,我已经走了很长的路。这是在J中生成rowland prime序列的一种更惯用的方式:

~. (#~ 1&<) | 2 -/\ (, ({: + ({: +. >:@#)))^:(1000 - #) 7

表达式(, ({: + ({: +. >:@#)))^:(1000 - #) 7生成最多1000个成员的所谓原始序列。该序列的第一个差异可以由| 2 -/\生成,即每两个元素的差异的绝对值。 (将其与原始问题中原始的,冗长的diffs动词进行比较。)

最后,我们删除了一些和重复的素数~. (#~ 1&<)以获得素数序列。

这远远优于我之前的做法。它很容易变成动词,通过一点递归生成n个素数。

答案 1 :(得分:2)

我还没有完整的答案,但是Roger Hui的this essay有一个默认的构造,你可以用它来代替显式的while循环。另一个(相关的)途径是将块的内部逻辑变成一个默认的表达式,如下所示:

FUNWITHTACIT =: ] , {: + {: +. 1 + #
rowland =: monad define
    result =. >a:
    t =. 7x
    while. (# result) < y do.
      t =. FUNWITHTACIT t
      d =.  | -/ _2 {. t
      result =. ~.result,((d>1)#d)
    end.
    result
)

(您可能希望保留if块以提高效率,因为我编写代码的方式是result被修改,无论条件是否满足 - 如果不是,修改没有效果。if逻辑甚至可以通过使用Agenda运算符写回到默认表达式。)

完整的解决方案包括找出如何将while循环中的所有逻辑表示为单个函数,然后使用Roger的技巧将while逻辑实现为默认表达式。我会看到我能发现什么。

顺便说一句,我让J为你构建FUNWITHTACIT,取代码的前几行,手动替换你为变量值声明的函数(我可以做,因为它们都在运行在不同方面的单个参数上),用t替换y的每个实例,并告诉J构建与结果表达式相等的默认值:

]FUNWITHTACIT =: 13 : 'y,({:y)+(1+#y)+.({:y)'
   ] , {: + {: +. 1 + #

使用13来声明monad是J知道如何获取monad(否则用你在程序中编写的3 : 0monad define显式声明)并将显式表达式转换为默认表达式

编辑:

以下是我在评论中提到的为avenue(2)编写的函数:

candfun =: 3 : '(] , {: + {: +. 1 + #)^:(y) 7'
firstdiff =: [: | 2 -/\ ]
triage =: [: /:~ [: ~. 1&~: # ]
rowland2 =: triage @firstdiff @candfun

此函数使用Rowland递归关系生成前n个候选数字,计算它们的第一个差异,丢弃所有第一个差异等于1,丢弃所有重复项,并按升序对它们进行排序。我认为这还不完全令人满意,因为论证设定了候选人数而不是结果数。但是,它仍然在进步。

示例:

   rowland2 1000
3 5 7 11 13 23 47 101 233 467 941

这是我发布的第一个函数的版本,将每个参数的大小保持在最小值:

NB. rowrec takes y=(n,an) where n is the index and a(n) is the
NB. nth member of the rowland candidate sequence. The base case
NB. is rowrec 1 7. The output is (n+1,a(n+1)).
rowrec =: (1 + {.) , }. + [: +./ 1 0 + ]

rowland3 =: 3 : 0
 result =. >a:
 t =. 1 7
 while. y > #result do.
  ts =. (<"1)2 2 $ t,rowrec t
  fdiff =. |2-/\,>(}.&.>) ts
  if. 1~:fdiff do.
   result =. ~. result,fdiff
  end.
  t =. ,>}.ts
 end.
 /:~ result
) 

找到第一个y-many不同的Rowland素数并按升序显示它们:

   rowland3 20
3 5 7 11 13 23 47 53 101 233 467 941 1889 3779 7559 15131 30323 60647 121403 242807

此功能的大多数复杂性来自我对盒装数组的处理。它不是漂亮的代码,但在计算过程中它只会在内存中保留4+#result个许多数据元素(以对数级增长)。原始函数rowland(#t)+(#result)元素保留在内存中(以线性比例增长)。 rowland2 y构建了一个y的数组 - 许多元素,这使得它的内存配置文件几乎与rowland相同,即使它永远不会超出指定的边界。我喜欢rowland2的紧凑性,但如果没有公式来预测生成n个多个不同质数所需的y的确切大小,则该任务需要在试错的基础上完成因此在冗余计算中可能会使用比rowlandrowland3更多的周期。 rowland3可能比我的rowland版本效率更高,因为FUNWITHTACIT在每次循环迭代时重新计算#t - rowland3只增加一个计数器,计算密集型。

尽管如此,我对rowland3明确的控制结构感到不满意。似乎应该有一种方法来使用递归或其他东西来实现这种行为。