J:牛顿法的隐含副词

7
我在'addons/math/misc/brent.ijs'中发现了Brent方法的实现,它是一个副词。我也想建立一个作为副词的牛顿迭代法,但这比建立省略动词要难得多。
以下是牛顿迭代法的显式版本:
   newton_i =: 1 : '] - u % u d.1'

使用方式如下:

   2&o. newton_i^:_ (1) NB. (-: 1p1) must be found
1.5708
   2 o. 1.5708 NB. after substitution we get almost 0
_3.67321e_6

当然,为了方便起见:
    newton =: 1 : 'u newton_i^:_'

什么是默示等效?

我认为在这种情况下,d.会阻止您编写一个暗示的副词。 - Eelvex
2
简短的答案是 n_i =: d.0 1 (%/@:) (-\) (]`) (`:6)newton =: n_i (^:_)`。稍后我会回来并解释为什么(我现在正在用手机)。 - Dan Bron
1
Danylo:因为副词列车是从左到右(即LIFO)构建的;将f(d.0 1)(%/@:)视为一个黑盒子,它有效地构建了(f%f d.1);那么你就得到了black_box(\-)(`]),反向阅读(LIFO)后读取],-,black_box,然后执行成为列车] - black_box。不,真正狡猾的技巧在于使用了d.0 1` :)。这样清楚了吗?还是需要我发布正式答案? - Dan Bron
丹,我完全理解了 f (d.0 1) (%/@:) 的技巧 :) - 确实很酷:我们计算 f 的零阶和一阶导数,然后在它们之间插入 %。我也理解了你写这三个副词的顺序。非常感谢! - Dan Oak
@Dan:正式地说,我希望得到一个不是评论的答案。如果您发布您的简短答案“newton_i =: d.0 1 (%/@:) (-) (]) (`:6)”,我会接受它。然后我会添加整个解释,就像我理解的那样。 - Dan Oak
显示剩余2条评论
2个回答

6

简而言之

根据评论,简短回答一下;隐含的等价于原始的显式newton_inewton分别是:

n_i =: d.0 1 (%/@:) (]`-`) (`:6) 
newton =: n_i (^:_)

一般而言,获取此类翻译的一些技巧可以在J论坛上找到。

构建

关键见解是(a)函数与其自身的“零阶导数”相同,以及(b)由于J语言的数组导向性质,我们可以同时计算一个函数的“零阶”和一阶导数。其余只是简单的收集信息。

在理想情况下,给定一个函数f,我们希望生成一个动词序列,如(] - f % f d. 1)。问题在于,J语言中的省略副词编程限制了我们必须生成一个动词,该动词仅一次提及输入函数(f)。

因此,我们使用一个巧妙的技巧:同时计算两个导数f: “零阶”导数(即恒等函数)和一阶导数。

   load 'trig'
   sin              NB. Sine function (special case of the "circle functions", o.)
1&o.

   sin d. 1 f.      NB. First derivative of sine, sin'.
2&o.

   sin d. 0 f.      NB. "Zeroeth" derivative of sine, i.e. sine.
1&o."0

   sin d. 0 1 f.    NB.  Both, resulting in two outputs.
(1&o. , 2&o.)"0

   znfd =: d. 0 1   NB. Packaged up as a re-usable name.
   sin znfd f.
(1&o. , 2&o.)"0

然后我们只需在它们之间插入一个分割线:
   dh =: znfd (%/@) NB. Quotient of first-derivative over 0th-derivattive

   sin dh
%/@(sin d.0 1)

   sin dh f.
%/@((1&o. , 2&o.)"0)

   sin dh 1p1  NB. 0
_1.22465e_16

   sin 1p1     NB. sin(pi) = 0
1.22465e_16
   sin d. 1 ] 1p1  NB. sin'(pi) = -1
_1
   sin dh 1p1  NB. sin(pi)/sin'(pi) = 0/-1 = 0
_1.22465e_16
< p > (%/@) 出现在 znfd 的右边,因为 J 语言中的默认副词编程是 LIFO(即从左到右,而“正常”的 J 是从右到左)。

集邮

正如我所说,剩余的代码只是集邮,使用标准工具构建一个动词序列,从原始输入中减去这个商:

   ssub  =: (]`-`) (`:6)     NB. x - f(x)

   +: ssub                   NB. x - double(x)
] - +:
   -: ssub                   NB. x - halve(x)
] - -:

   -: ssub 16                NB. 16 - halve(16)
8
   +: ssub 16                NB. 16 - double(16)
_16
   *: ssub 16                NB. 16 - square(16)
_240
   %: ssub 16                NB. 16 - sqrt(16)
12

因此:
    n_i =: znfd ssub         NB. x - f'(x)/f(x)

最后,使用^:_"应用到不动点"功能,我们得到:

    newton =: n_i (^:_)

看这里。


1
非常好的方法和解决方案。 - Eelvex
@DanBron,我已经根据我的理解在你的答案中添加了解释。你收到了吗? - Dan Oak
@Danylo,我没有看到,也许在我被通知之前已被其他用户拒绝了?你想添加一个答案,我可以复制粘贴吗? - Dan Bron
@DanBron,不好意思,我太懒了。你在这里的解释(http://www.jsoftware.com/pipermail/programming/2010年11月/021172.html)很棒。 - Dan Oak
@DanBron,最近我找到了我的被拒绝的编辑:http://stackoverflow.com/review/suggested-edits/6149041。我想知道那些不熟悉问题的人是如何评价的。 - Dan Oak
2
另外,请注意,这可以很容易地扩展到具有更多变量的函数:n_i =: D.0 1 (%./@:) (]\-`) (`:6)`。 - jpjacobs

1
 newton_i =: 1 : '] - u % u d.1'

半隐式(semi-tacit)是指当一个副词与动词绑定在一起时,产生了一个隐含动词(该副词在绑定时消失)。

 newton_i2 =: 1 : '(] - u % u d.1) y'

完全显式是指使用动词进行绑定不会解决副词的问题。

 + 1 : 'u/'

+/

 + 1 : 'u/ y'

+ (1: 'u/ y')

将半隐式副词完全隐式化并没有太大的重要性,因为可能没有性能提升,并且它具有与完全显式副词相同的好处,即在副词语境中而不是在调用者中解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接