简体   繁体   English

在不使用Math.sin函数的情况下用Java实现Sine

[英]Implement Sine in Java without Math.sin function

I'm trying to implement the sine function in Java without using Math.sin(x) . 我正在尝试在Java中实现正弦函数而不使用Math.sin(x) So I'm trying to realize this with the Taylor series. 因此,我试图通过泰勒系列来实现这一点。 Unfortunately, this code gives the wrong result(s). 不幸的是,这段代码给出了错误的结果。

If you don't know what the Taylor series is, have a look: 如果您不知道泰勒级数是什么,请看一下:

Here's the code snippet I created: 这是我创建的代码片段:

public static double sin(double a) {
   double temp = 1;
   int denominator = -1;
   if(a == Double.NEGATIVE_INFINITY || !(a < Double.POSITIVE_INFINITY)) {
      return Double.NaN;
   } 
   if(a != 0) {
      for (int i = 0; i <= a; i++) {
         denominator += 2;
         if(i % 2 == 0) {
            temp = temp + (Math.pow(a, denominator) / Factorial.factorial(denominator));
         } else {
            temp = temp - (Math.pow(a, denominator) / Factorial.factorial(denominator));
         }
      }
   }
   return temp;
}

I can't find the mistake I did. 我找不到我犯的错误。 Do you? 你呢?

Your issue is that you are using the value that is to be evaluated for the sine function as the limit for the denominator. 您的问题是您将正弦函数要评估的值用作分母的极限。 A Taylor series is evaluated as the limit of the function approaches infinity. 当函数的极限接近无穷大时,将评估泰勒级数。 You are only evaluating it in this case to the size of the input value, which doesn't really make sense. 在这种情况下,您仅根据输入值的大小对其进行评估,这实际上没有任何意义。 You should replace your for loop comparison with i < x where x is a constant representing however precise you wish to make it (the function will be fairly accurate for a value as low as 20 or so). 您应该将i < x for循环比较替换for i < x ,其中x是一个常数,表示您希望实现的精度(该函数对于值低至20左右的值都是相当准确的)。

There are two main issues in your code. 您的代码中有两个主要问题。 First issue is that you are looping i from 0 to a . 第一个问题是,你是循环i0a This means that, if a is a negative value, the for loop does not even start and your result will always be 1.0 . 这意味着,如果a为负值,则for循环甚至不会启动,并且结果始终为1.0 Whereas if a is positive, the loop starts, but it stops after (int) a iterations, and it doesn't make much sense, since the Taylor apporximation works fine when iterations n tends to infinity. 而如果a为正,则循环开始,但在(int) a迭代后停止,这没有多大意义,因为当n趋于无穷大时,泰勒逼近效果很好。

Second major issue is you are not making enough controls on the input value a . 第二个主要问题是您没有对输入值a进行足够的控制。 As I already said in Python: Calculate sine/cosine with a precision of up to 1 million digits 正如我在Python中已经说过的:计算正弦/余弦的精度高达一百万个数字

The real Taylor expansion centered in x 0 is: x 0为中心的实际泰勒展开式是:

在此处输入图片说明

where Rn is the Lagrange Remainder Rn拉格朗日余数

在此处输入图片说明

Note that Rn grows fast as soon as x moves away from the center x0 . 请注意,一旦x远离中心x0Rn就会快速增长。

Since you are implementing the Maclaurin series (Taylor series centered in 0) and not the general Taylor series , your function will give really wrong results when trying to calculate sin(x) for big values of x . 既然你正在实施麦克劳林系列 (泰勒级数0为中心),而不是一般的泰勒级数 ,你的函数将试图计算x的值大的sin(x)在给真正错误的结果。

So before the for loop you must reduce the domain to at least [-pi, pi] ... better if you reduce it to [0, pi] and take advantage of sine's parity. 因此,在for循环之前,必须将域至少减小到[-pi,pi] ...如果将其减小到[0,pi]并利用正弦奇偶校验则更好。

Working code: 工作代码:

public static double sin(double a) {

    if (a == Double.NEGATIVE_INFINITY || !(a < Double.POSITIVE_INFINITY)) {
        return Double.NaN;
    }

    // If you can't use Math.PI neither,
    // you'll have to create your own PI
    final double PI = 3.14159265358979323846;

    // Fix the domain for a...

    // Sine is a periodic function with period = 2*PI
    a %= 2 * PI;
    // Any negative angle can be brought back
    // to it's equivalent positive angle
    if (a < 0) {
        a = 2 * PI - a;
    }
    // Also sine is an odd function...
    // let's take advantage of it.
    int sign = 1;
    if (a > PI) {
        a -= PI;
        sign = -1;
    }
    // Now a is in range [0, pi].


    // Calculate sin(a)

    // Set precision to fit your needs.
    // Note that 171! > Double.MAX_VALUE, so
    // don't set PRECISION to anything greater
    // than 84 unless you are sure your
    // Factorial.factorial() can handle it
    final int PRECISION = 50;
    double temp = 0;
    for (int i = 0; i <= PRECISION; i++) {
        temp += Math.pow(-1, i) * (Math.pow(a, 2 * i + 1) / Factorial.factorial(2 * i + 1));
    }

    return sign * temp;

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM