泰勒级数的正弦近似值

时间:2019-08-23 10:38:22

标签: prolog numeric numerical-methods approximation polynomial-approximations

我有一些代码应该通过泰勒级数定义找到 sin(15°) 的近似值,并将其与内置函数sin进行比较。

我有不同的结果。

  • X-弧度值
  • R-系列和的当前值
  • Eps-准确性值
  • S-签名
  • F-N的阶乘值
  • XN-X的值乘以N的幂
  • N-功率1,3,5 ...
% base
sin_taylor(_,R,Eps,S,FN,XN,_) :-
    abs(S*XN/FN) < Eps,
    R = 0, !.

% step
sin_taylor(X, R, Eps, S, FN, XN, N) :-
    S1 = S*(-1),
    N1 = N+1,
    FN1 = FN*(2*N+2)*(2*N+3),
    XN1 = XN*X*X,
    sin_taylor(X,R1,Eps,S1,FN1,XN1,N1),
    R is R1+S*XN/FN.

% auxiliary predicate to supply parameters to the main one
sin(X,R,Eps) :-
    sin_taylor(X,R1,Eps,-1,1,X*X,1),
    R is 1+R1.

控制台中的结果

?- X is 15*(pi / 180), sin(X,R,0.0001).
   X = 0.2617993877991494,
   R = 0.931695959721973.

?- X is 15*(pi / 180), R0 is sin(X).
   X = 0.2617993877991494,
   R0 = 0.25881904510252074.

2 个答案:

答案 0 :(得分:2)

无法在您的代码中识别正确的n项...阶乘计算隐藏在哪里?请记住,早期的优化是软件工程中所有弊端的根源:)

Wikipedia中列出的解决方案的直接翻译使我想到了这段代码(嗯,应该在谓词名称中引用Maclaurin Taylor ...)

class C extends ReactComponent {

   constructor() {
      var handleMerging = handleMerging.bind(this);

      handleMerging().then(...);
   }
}

给出合理的结果:

:- module(sin_taylor,
          [sin/3
          ,rad_deg/2
          ,fact/2
          ]).

% help predicate to give parameters to the main one
sin(X,R,Eps):-
    syn_taylor(X,Eps,0,R).

syn_taylor(X,Eps,N,R) :-
    S is -1**N,
    T is 2*N+1,
    fact(T,D),
    E is X**T,
    Q is S*E/D,
    (   Q < Eps
    ->  R = Q
    ;   M is N+1,
        syn_taylor(X,Eps,M,R1),
        R is Q+R1
    ).

rad_deg(R,D) :-
    var(R) -> R is D*(pi / 180).
    % tbd compute D from R

fact(N,F) :- N>0 -> N1 is N-1, fact(N1,F1), F is N*F1 ; F=1.

答案 1 :(得分:2)

如果从最小的求和数开始,然后使用霍纳模式,则可获得更好的结果,并且数值误差更少。但是问题是,如何找出哪个索引MainWindow应该给您最小的求和数k

对于ak,被乘数为sin/1

对于an = (-1)n * x(2*n+1) / (2n+1)!,其中x = u * 10(-v)u的尾数,而xv的负指数,我们看到:

x

因此,如果我们想要|an| < |x(2*n+1)| = 10((log10(|u|) - v) * (2*n+1))位数字,我们可以尝试以最小的方式增加索引

p

如果我们已经有k = ceiling(p / (2 * (log10(|u|) - v)))的问题,这将是最好的选择。最小的求和索引|x| =< 1/2在这里计算:

k

谓词mp_sin(X, P, Y) :- K is integer(ceiling(requested(P)/(2*dec_log10(X)))), init_sin(K, X, P, U), mp_sin(U, X, P, Y). init_sin/4然后从最小的被加数到最大的被加数mp_sin/4向后计算正弦值,并使用Horner模式同时获得和:

a0

内部谓词mp_sin((0, S), _, _, S) :- !. mp_sin(U, X, P, Y) :- next_sin(U, X, P, V), mp_sin(V, X, P, Y). init_sin(K, X, P, (K, S)) :- (K mod 2 =:= 0 -> V = X; V = -X), mp_math(V/(2*K+1), P, S). next_sin((L, T), X, P, (K, S)) :- K is L-1, (K mod 2 =:= 0 -> V = X; V = -X), mp_math((T*X*X/(2*K+2)+V)/(2*K+1), P, S). 在这里用于BigDecimal算术,因此我们还可以以mp_math/3的精度来计算sin/1,这对于常规浮点数是不可能的。结果如下:

p = 100

由于所有computations均以给定的精度完成,因此当您使用Horner模式进行向后计算时,只需要很少的位。更少的数字错误意味着您可以使用更少的位来驱动计算。但是,您仍然需要一点额外的精度。

要获得Jekejeke Prolog 4, Runtime Library 1.4.1 (20 August 2019) ?- use_module(library(decimal/multi)). % 7 consults and 0 unloads in 222 ms. Yes ?- X is mp(15*(pi/180),5), R is mp(sin(X),5). X = 0d0.26181, R = 0d0.25883 ?- X is mp(15*(pi/180),102), R is mp(sin(X),102). X = 0d0.2617993877991494365385536152 732919070164307832812588184145787160 256513671905174165523362354451764223 32, R = 0d0.2588190451025207623488988376 240483283490689013199305138140032073 150569747488019969223679746942496655 21 的结果,我正在使用p = 4,并且得到了p = 5的结果,我正在使用p = 100。尚未有时间找到这种额外精度的启发式方法,并使它对最终用户透明。通常,我们可以使用较小的p = 102,因此仍在进行中。