逼近正弦和余数

时间:2015-04-04 11:01:48

标签: c++ c math

练习的目标是使用Maclaurin系列公式评估sin

#include <stdio.h>
#include <math.h>

double factorial(int n);

int main(void) {

    double x, p, r;
    int i, n;

    printf("Enter a positive double number and a non - negative integer : \n");
    scanf("%lf%d", &x, &n);

    if (x <= 0) {
        printf("Error: first argument must be a positive double.\n");
        return -1;
    }

    if (n < 0) {
        printf("Error: second argument must be a non - negative integer.\n");
        return -1;
    }

    p = 0;
    for (i = 0; i <= n; i++)
    {
        p += pow(-1, i) / factorial(2 * i + 1) * pow(x, 2 * i + 1);
    }

    r = fabs(sin(x) - p);

    printf("The %d-th order Maclaurin polynomial function at x=%f is %f, with an error approximation of %f.\n", n, x, p, r);



    getch();
    return 0;
}

double factorial(int n)
{
    int i;
    long result = 1;
    for (i = 1; i <= n; i++)
        result *= i;

    return result;
}

我输入“12 16”的结果很奇怪。为什么呢?

5 个答案:

答案 0 :(得分:4)

这里有三个问题。

  • 正如Mukit Chowdhury回答的那样,多头不能容纳大量的因子。但是,正如GregS评论的那样,16!应该没问题。使用长按21时,你应该会得到奇怪的结果!或更高,但您不需要更改此输入的因子函数。不过,您可能应该使用类似下面的内容:

    double factorial(int n)
    {
        int i;
        double result = 1;
        for (i = 1; i <= n; i++)
            result *= i;
        return result;
    }
    
  • 在输入“12 16”上,您的代码声称正在计算16阶Maclaurin多项式,但它计算的是33阶Maclaurin多项式。 16阶多项式的项最多为-x ^ 15/15! + 0x ^ 16。解决这个问题的一种方法是纠正for循环,如下所示:

    for (i = 1; i <= n; i+=2)
    {
        p += pow(-1, (i-1)/2) / factorial(i) * pow(x, i);
    }
    

    因此,您的代码遇到了阶乘问题,但这只是因为您正在计算额外的术语。如果计算最多-x ^ 15/15!的项,您应该能够正确计算多项式的值。

  • 12阶的16阶Maclaurin多项式的实际值是-4306.756 ......这可能不是你所期望的,这可能是练习的一部分。为了得到准确的近似值,你应该期望你需要最后一个小项,所以你需要n!超过x ^ n。通过斯特林的近似,n! 〜(n / e)^ n,所以你想要n&gt; e * x,其中e = 2.71828 ...,因此n> = 33。此时,误差为0.005,并且n乘以c会使误差的大小减小大约e ^ c倍。
  • 当您在产生较小的最终结果的路上减去大数时,您应该期望双精度算术中的大错误。这可能不是问题,因为最大的术语大小只有2 ^ 14。你仍然可以获得足够的精确度,你不会注意到你不能通过添加更多的术语来接近罪恶(12)。

答案 1 :(得分:3)

MacLauring的两个术语或正弦的泰勒系列之间的商是

-x*x/(2*k*(2k+1))

可以利用这一点来避免所有权力和因子及其溢出。

mxx = -x*x;
term = mxx / 6;
sum = 1+term;
k=2;
while( not good enough )
    term = term*mxx/(2*k*(2*k+1));
    sum = sum + term;
    k = k+1;

return sum*x

答案 2 :(得分:3)

在这里我会做很多不同的事情。

我有点偏向于不得不编写数字软件,不仅能得到正确的结果而且能快速得到它们,但我在这里看到了很多浪费的计算。例如,考虑该系列的两个连续的非零项 (x ^ 13)/(13!)和 - (x ^ 15)/(15!)。如果你已经知道了它的价值 (x ^ 13)/(13!),你需要做多少计算才能获得 - (x ^ 15)/(15!)? 答案是,比计算(x ^ 13)/(13!)要少得多。 首先。如果你盲目地遵循通常的公式 对于MacLaurin系列并重新计算每个新术语的阶乘, 为了得到15!你将重复你已经做过的所有计算 为13!,然后只执行两次新的乘法。 要有效地计算这个系列而不浪费计算和没有 引入不必要的大数(以及所有可能的问题 他们可以导致,即使你使用浮点数,) 只看一个非零项之间的比例 而下一个。它易于计算,不涉及拖入 pow函数。 一个好的算法可以让你轻松地增加术语数量,直到最后一个 term是浮点数的精度, 只要你使用合理的x输入值。 (没有必要x大于2 * pi,或者真的如此 没有任何理由将x设置为大于pi / 2,因为任何更大的正弦值 x的值可以从该范围内输入的正弦值中找到。)

答案 3 :(得分:2)

以下是我用Java编写的方法(易于转换为C ++):

package math;

/**
 * Sine using Maclauren series
 * @author Michael
 * @link http://stackoverflow.com/questions/29445615/approximating-of-sine-and-the-remainder
 * @since 4/4/2015 8:37 PM
 */
public class Sine {

    private static final double TWO_PI = 2.0*Math.PI;
    private static final int numPoints = 21;
    private static final int numTerms = 21;

    public static void main(String[] args) {
        double x = -Math.PI;
        double dx = 2.0*Math.PI/(numPoints-1);
        for (int i = 0; i < numPoints; ++i) {
            System.out.println(String.format("# terms: %d angle (radians) %10.6f sine: %15.10f", numTerms, x, sine(x, numTerms)));
            x += dx;
        }
    }

    public static double sine(double radians, int numTerms) {
        double value = 0.0;
        // Start by making sure the angle -pi/2 <= x <= +pi/2
        double x = normalizeAngle(radians);
        double term = x;
        for (int n = 3; n < numTerms; n += 2) {
            value += term;
            term *= -x*x/n/(n-1);
        }
        return value;
    }

    public static double normalizeAngle(double radians) {
        return radians - TWO_PI*Math.floor((radians+Math.PI)/TWO_PI);
    }
}

这是输出:

java math.Sine
# terms: 21 angle (radians)  -3.141593 sine:   -0.0000000224
# terms: 21 angle (radians)  -2.827433 sine:   -0.3090169974
# terms: 21 angle (radians)  -2.513274 sine:   -0.5877852526
# terms: 21 angle (radians)  -2.199115 sine:   -0.8090169944
# terms: 21 angle (radians)  -1.884956 sine:   -0.9510565163
# terms: 21 angle (radians)  -1.570796 sine:   -1.0000000000
# terms: 21 angle (radians)  -1.256637 sine:   -0.9510565163
# terms: 21 angle (radians)  -0.942478 sine:   -0.8090169944
# terms: 21 angle (radians)  -0.628319 sine:   -0.5877852523
# terms: 21 angle (radians)  -0.314159 sine:   -0.3090169944
# terms: 21 angle (radians)   0.000000 sine:    0.0000000000
# terms: 21 angle (radians)   0.314159 sine:    0.3090169944
# terms: 21 angle (radians)   0.628319 sine:    0.5877852523
# terms: 21 angle (radians)   0.942478 sine:    0.8090169944
# terms: 21 angle (radians)   1.256637 sine:    0.9510565163
# terms: 21 angle (radians)   1.570796 sine:    1.0000000000
# terms: 21 angle (radians)   1.884956 sine:    0.9510565163
# terms: 21 angle (radians)   2.199115 sine:    0.8090169944
# terms: 21 angle (radians)   2.513274 sine:    0.5877852526
# terms: 21 angle (radians)   2.827433 sine:    0.3090169974
# terms: 21 angle (radians)   3.141593 sine:   -0.0000000224

Process finished with exit code 0

答案 4 :(得分:0)

你的factorial()返回,而它的返回类型是 double

最重要的是,不能包含 16!。哪个加倍可以。