【问题标题】:How can I generate the Rowland prime sequence idiomatically in J?如何在 J 中惯用地生成罗兰素数序列?
【发布时间】:2010-06-10 08:53:19
【问题描述】:

如果您不熟悉罗兰素数序列,可以通过here 了解它。我在 J 中创建了一个丑陋的程序单子动词来生成此序列中的前 n 个术语,如下所示:

rowland =: monad 定义
    结果=。 0 $ 0
    t =。 1 美元 7
    尽管。 (# 结果)  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 
    

【问题讨论】:

    标签: tacit-programming j


    【解决方案1】:

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

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

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

    最后,我们删除那些和重复的素数~. (#~ 1&amp;&lt;),得到素数序列。

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

    【讨论】:

    • 7:`(([ + +.)~ $:@&lt;:)@.(1 &lt; ]) 用于初始序列,~. 1-.~ 用于删除 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,方法是获取代码的前几行,手动替换为变量值声明的函数(我可以这样做,因为它们都在单个参数以不同的方式),用y替换t的每个实例,并告诉J构建结果表达式的默认等价物:

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

    使用 13 声明 monad 是 J 知道获取 monad 的方式(否则使用 3 : 0monad 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 构建了一个由y-many 元素组成的数组,这使得它的内存配置文件几乎与rowland 相同,即使它永远不会超过指定的范围。我喜欢 rowland2 的紧凑性,但没有公式来预测生成 n 多个不同素数所需的 y 的确切大小,该任务需要在反复试验的基础上完成,因此可能使用在冗余计算上比rowlandrowland3 更多的周期。 rowland3 可能比我的rowland 版本更有效,因为FUNWITHTACIT 在每次循环迭代时都会重新计算#t -- rowland3 只是增加一个计数器,计算量较小。

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

    【讨论】:

    • 非常有趣的东西!我还在消化它……
    • 不客气! J 是我最喜欢的语言,所以就我而言,任何探索它的机会都是一种胜利。目前我正在探索两种途径,其中一种与您的原始程序非常接近,另一种则有些遥远。 (1) 我们是否需要保留t 的所有先前历史记录?如果没有,只保留我们在每一步生成的最后两个值可能会更有效。 (2) 我们可以将t 输出为 n 个值,然后在最后使用# 对它们进行分类。
    • 我刚刚在我的答案中添加了一个新功能。
    • 我刚刚在 MAA 网站 (maa.org/mathtourist/mathtourist_8_8_08.html) 上看到,Rowland 公式在生成 (p-3)/2 个多个 1 后生成素数 p。您认为我们可以使用该信息(或类似信息)来推导出 rowland2 的论点,该论点将准确说明要检查多少候选人? (编辑)顺便说一句,我也是 Project Euler 的用户。我最初是通过在回答论坛中看到 Roger Hui 的出色解决方案后才发现 J 的。他的代码确实令人印象深刻。
    • 好吧,我不认为我们可以使用(p - 3) / 2,因为随后的引述说罗兰“有一种方法可以跳过那些无用的 1,但这样做本质上需要对素数进行独立测试。”现在,我确实认为在 t 中只保留最后两项的优化是完全有效的,因为这就是我们所需要的。
    猜你喜欢
    • 1970-01-01
    • 2015-07-22
    • 2017-09-27
    • 2021-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-25
    • 2016-11-21
    相关资源
    最近更新 更多