【数学】Sin, Cos関数を自作する

微積分を勉強していたらオイラーの公式を知り、そこからマクローリン展開でCos, Sinが求められることを知ったので試しに C# で実装してみる。
ほぼ近い値が出ているが実務にあたっては標準ライブラリを使えばいいのでパフォーマンスなどは考慮していない

```csharp
using System;

public static class Trigonometric
{
    /// <summary>
    /// n の p乗 n^p を求める。(0乗である場合は1を返す)
    /// </summary>
    static double Pow(double n, int p)
    {
        if (p == 0)
        {
            return 1;
        }

        double r = n;
        for (int i = 0; i < p - 1; ++i)
        {
            r *= n;
        }

        return r;
    }

    /// <summary>
    /// n の階乗 n! を求める (n=0 で 0!となる場合は1を返す)
    /// </summary>
    static double Factorial(int n)
    {
        if (n == 0)
        {
            n = 1;
        }

        double r = n;
        for (int i = (n - 1); i > 0; --i)
        {
            r *= i;
        }

        return r;
    }

    /// <summary>
    /// マクローリン展開された項 x^p / p! を求める
    /// </summary>
    /// <param name="x"></param>
    /// <param name="p"></param>
    /// <returns></returns>
    static double Arg(double x, int p)
    {
        return Pow(x, p) / Factorial(p);
    }

    static double Cos(double x)
    {
        x %= (2 * Math.PI); // 2π で剰余算して 2π を超えないようにする

        double sum = 0;
        int n = 0;
        // 0^0 / 0 = 1/1
        // 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! ... 
        for (int i = 0; i < 30; ++i)
        {
            sum += Arg(x, n);
            n += 2;
            sum -= Arg(x, n);
            n += 2;
        }

        return sum;
    }
    
    static double Sin(double x)
    {
        x %= (2 * Math.PI); // 2π で剰余算して 2π を超えないようにする

        double sum = 0;
        int n = 1;
        // 0^0 / 0 = 1/1
        // x - x^3/3! + x^5/5! - x^7/7! + x^9/9! ... 
        for (int i = 0; i < 30; ++i)
        {
            sum += Arg(x, n);
            n += 2;
            sum -= Arg(x, n);
            n += 2;
        }

        return sum;
    }


    public static void Main()
    {
        for (int i = 0; i <= 18; ++i)
        {
            double rad2Deg = (Math.PI / 180);
            double degree = (i * 10);
            Console.WriteLine($"Cos({degree}) = {Cos(rad2Deg * degree)}, {Math.Cos(rad2Deg * degree)}");
            Console.WriteLine($"Sin({degree}) = {Sin(rad2Deg * degree)}, {Math.Sin(rad2Deg * degree)}");
        }
    }
}

```

<br>

### おまけ
C# でも `System.Numerics.Complex` を使えば複素数が使えるようだったので試しにオイラーの公式を作ってみた

```csharp
    /// <summary>
    /// オイラーの公式 e^{xi} をマクローリン展開した値を返す
    /// </summary>
    /// <param name="x"></param>
    /// <returns></returns>
    static Complex EulerFormula(double x)
    {
        // 虚数i
        Complex i = new Complex(0, 1);
        // iをx倍した値(ix)
        Complex ix = x * i;

        Complex sum = 0;
        int n = 0;
        // 0^0 / 0! = 1/1 = 1
        // 1 + ix - x^2/2! - x^3i/3! + x^4/4! + x^5i/5! - x^6/6! - x^7i/7! + x^8/8! ... 
        for (int j = 0; j < 30; ++j)
        {
            sum += Arg(ix, n);
            n++;
            sum += Arg(ix, n);
            n++;
        }

        return sum;
    }

    public static void Main()
    {
        for (int i = 0; i <= 36; ++i)
        {
            double deg = 10 * i;
            double ang = (Math.PI / 180) * deg;
            // e^{ix} = cos(x) + i sin(x)
            Complex eix = EulerFormula(ang);

            Console.WriteLine($"{deg}° = eix: {eix}, cos: {Math.Cos(ang)}, sin: {Math.Sin(ang)}");
        }
    }
```

微妙に違うがだいたい近い値が出た

```
0° = eix: (1, 0), cos: 1, sin: 0
10° = eix: (0.984807753012208, 0.17364817766693), cos: 0.984807753012208, sin: 0.17364817766693
20° = eix: (0.939692620785909, 0.342020143325669), cos: 0.939692620785908, sin: 0.342020143325669
30° = eix: (0.866025403784439, 0.5), cos: 0.866025403784439, sin: 0.5
40° = eix: (0.766044443118978, 0.642787609686539), cos: 0.766044443118978, sin: 0.642787609686539
50° = eix: (0.642787609686539, 0.766044443118978), cos: 0.642787609686539, sin: 0.766044443118978
60° = eix: (0.5, 0.866025403784438), cos: 0.5, sin: 0.866025403784439
70° = eix: (0.342020143325669, 0.939692620785908), cos: 0.342020143325669, sin: 0.939692620785908
80° = eix: (0.17364817766693, 0.984807753012208), cos: 0.17364817766693, sin: 0.984807753012208
90° = eix: (4.60089142957004E-17, 1), cos: 6.12323399573677E-17, sin: 1
100° = eix: (-0.17364817766693, 0.984807753012208), cos: -0.17364817766693, sin: 0.984807753012208
110° = eix: (-0.342020143325669, 0.939692620785908), cos: -0.342020143325669, sin: 0.939692620785908
120° = eix: (-0.5, 0.866025403784439), cos: -0.5, sin: 0.866025403784439
130° = eix: (-0.642787609686539, 0.766044443118978), cos: -0.642787609686539, sin: 0.766044443118978
140° = eix: (-0.766044443118978, 0.642787609686539), cos: -0.766044443118978, sin: 0.642787609686539
150° = eix: (-0.866025403784439, 0.5), cos: -0.866025403784439, sin: 0.5
160° = eix: (-0.939692620785908, 0.342020143325668), cos: -0.939692620785908, sin: 0.342020143325669
170° = eix: (-0.984807753012208, 0.17364817766693), cos: -0.984807753012208, sin: 0.17364817766693
180° = eix: (-1, -1.11283540153634E-16), cos: -1, sin: 1.22464679914735E-16
190° = eix: (-0.984807753012208, -0.173648177666931), cos: -0.984807753012208, sin: -0.17364817766693
200° = eix: (-0.939692620785908, -0.342020143325669), cos: -0.939692620785908, sin: -0.342020143325669
210° = eix: (-0.866025403784439, -0.500000000000001), cos: -0.866025403784439, sin: -0.5
220° = eix: (-0.766044443118977, -0.64278760968654), cos: -0.766044443118978, sin: -0.642787609686539
230° = eix: (-0.64278760968654, -0.766044443118978), cos: -0.642787609686539, sin: -0.766044443118978
240° = eix: (-0.500000000000002, -0.866025403784438), cos: -0.5, sin: -0.866025403784438
250° = eix: (-0.342020143325669, -0.939692620785909), cos: -0.342020143325669, sin: -0.939692620785908
260° = eix: (-0.173648177666929, -0.984807753012209), cos: -0.17364817766693, sin: -0.984807753012208
270° = eix: (1.30336761991319E-15, -1), cos: -1.83697019872103E-16, sin: -1
280° = eix: (0.17364817766693, -0.984807753012206), cos: 0.17364817766693, sin: -0.984807753012208
290° = eix: (0.34202014332567, -0.939692620785911), cos: 0.342020143325669, sin: -0.939692620785908
300° = eix: (0.500000000000002, -0.866025403784441), cos: 0.5, sin: -0.866025403784439
310° = eix: (0.642787609686543, -0.766044443118985), cos: 0.642787609686539, sin: -0.766044443118978
320° = eix: (0.766044443118982, -0.642787609686541), cos: 0.766044443118978, sin: -0.64278760968654
330° = eix: (0.866025403784442, -0.500000000000002), cos: 0.866025403784438, sin: -0.5
340° = eix: (0.939692620785915, -0.342020143325672), cos: 0.939692620785908, sin: -0.342020143325669
350° = eix: (0.984807753012194, -0.173648177666933), cos: 0.984807753012208, sin: -0.17364817766693
360° = eix: (1, -1.09099622632254E-14), cos: 1, sin: -2.44929359829471E-16
```