1

如果您不熟悉 Rowland 素数序列,可以在此处找到相关信息。我在 J 中创建了一个丑陋的程序单子动词来生成该序列中的前n 个术语,如下所示:

rowland =: monad 定义
    结果=。0 $ 0
    t =。1 美元 7
    尽管。(# 结果) < 是的。
        一个=。{:吨
        n =。1 + # 吨
        t =。t , a + n +。一种
        d =。| -/ _2 {。吨
        如果。d > 1 做。
            结果=。~。结果
        结尾。
    结尾。
    结果
)

这非常有效,它确实生成了序列中的前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'
4

2 回答 2

3

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

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

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

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

这比我以前做的方式要好得多。它可以很容易地变成一个动词,通过n少量递归生成多个素数。

于 2012-02-20T17:05:24.730 回答
2

我还没有一个完整的答案,但是Roger Hui 的这篇文章有一个默认的结构,你可以用它来替换显式的 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,手动替换为变量值声明的函数(我可以这样做,因为它们都以不同的方式对单个参数进行操作) ,替换了twith的每个实例,y并告诉 J 构建结果表达式的默认等价物:

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

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

编辑:

以下是我在评论中提到的为大道 (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 个不同的 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构建一个 -many 元素的数组y,这使得它的内存配置文件几乎相同,rowland即使它永远不会超过指定的界限。我喜欢rowland2它的紧凑性,但没有一个公式来预测生成 n 个不同素数所需的确切大小y,该任务将需要在试错的基础上完成,因此可能会使用比rowland或更多rowland3的周期冗余计算。rowland3可能比我的版本更有效rowland,因为在每次循环迭代时重新计算——FUNWITHTACIT只是增加一个计数器,计算量较小。#trowland3

rowland3不过,我对' 的显式控制结构并不满意。似乎应该有一种方法可以使用递归或其他方法来完成此行为。

于 2010-06-12T13:10:42.070 回答