π

目次

Unknown programmer's programming note.

<2022-09-26 月>

1. プログラムの概要

1.1. Machinの公式

pi1 - Machinの公式を使って円周率を求める

Machinの公式:

\(\frac{\pi}{4} = 4 \arctan{\frac{1}{5}} - \arctan{\frac{1}{239}}\)

arctanは、次の級数展開を使えばよい:

\(\arctan{x} = x - \frac{x^3}{3} + \frac{x^5}{5} - \frac{x^7}{7} + ...\)

1.2. 相加相乗平均

pi2 - 相加相乗平均を使って円周率を求める

\(\pi_n = \frac{2 a_{n+1}^2}{1 - \sum_{k=0}^{n} {2^k (a_k^2 - b_k^2)}} = \frac{2 a_{n+1}^2}{1 - \sum_{k=0}^{n}{2^k (a_k - a_{k-1})^2}}\)

2. C

2.1. Machinの公式

/*
 * 出典元「C言語による最新アルゴリズム事典」奥村晴彦著 - P16
 * インデントは変更してあります。
 * 他にも、メッセージや変数名や処理を変更している場合があります。
 */

#include <stdio.h>

int main()
{
    int k;
    long double p, t, last;

    p = 0;
    k = 1;
    t = 16.0L / 5.0L;

    do
    {
        last = p;
        p += t / k;
        t /= -5.0L * 5.0L;
        k += 2;
    } while (p != last);

    k = 1;
    t = 4.0L / 239.0L;

    do
    {
        last = p;
        p -= t / k;
        t /= -239.0L * 239.0L;
        k += 2;
    } while (p != last);

    printf("%.30Lf\n", p);
}

2.2. 相加相乗平均

/*
 * 出典元「C言語による最新アルゴリズム事典」奥村晴彦著 - P16
 * インデントは変更してあります。
 * 他にも、メッセージや変数名や処理を変更している場合があります。
 */

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

int main()
{
    int i;
    double a, b, s, t, last;

    a = 1;
    b = 1 / sqrt(2.0);
    s = 1;
    t = 4;

    for (i = 0; i < 3; i++)
    {
        last = a;
        a = (a + b) / 2;
        b = sqrt(last * b);
        s -= t * (a - last) * (a - last);
        t *= 2;
        printf("%16.14f\n", (a + b) * (a + b) / s);
    }
}

3. Emacs Lisp

3.1. Machinの公式

(let ((k 1)
      (p 0)
      (x (/ 16.0 5.0))
      (last))

  (while (progn
           (setq last p
                 p (+ p (/ x k))
                 x (/ x (* -5.0 5.0))
                 k (+ k 2))
           (not (= p last))
           ))

  (setq k 1
        x (/ 4.0 239.0))

  (while (progn
           (setq last p
                 p (- p (/ x k))
                 x (/ x (* -239.0 239.0))
                 k (+ k 2))
           (not (= p last))
           ))

  (message "%.20f" p))

whileが奇妙な形に見えるかもしれません。 これは do-while あるいは repeat-until を真似るためのものです。

3.2. 相加相乗平均

(let ((a 1)
      (b (/ 1.0 (sqrt 2.0)))
      (s 1)
      (x 4))

  (message (with-output-to-string
             (dotimes (i 3)
               (setq last a
                     a (/ (+ a b) 2)
                     b (sqrt (* last b))
                     s (- s (* x (* (- a last) (- a last))))
                     x (* x 2))
               (princ (format "%16.14f\n" (/ (* (+ a b) (+ a b)) s)))))))

4. Bash

bashは浮動小数点数の計算ができません。 bcにお願いするのが代表的な解決策です。

4.1. Machinの公式

#!/bin/bash

function fmath {
    echo "scale=20; $1" | bc
}

p=0
k=1
t=$(fmath "16.0 / 5.0") 

while
    last=$p
    p=$(fmath "$p + $t / $k")
    t=$(fmath "$t / (-5.0 * 5.0)")
    k=$(( $k + 2 ))
    test $(fmath "$p != $last") -eq 1
do true; done

k=1
t=$(fmath "4.0 / 239.0")

while
    last=$p
    p=$(fmath "$p - $t / $k")
    t=$(fmath "$t / (-239.0 * 239.0)")
    k=$(( $k + 2 ))
    test $(fmath "$p != $last") -eq 1
do true; done

printf "%.30Lf\n" $p

whileループが異様な形をしています。 これは、必ず一回は本体を実行してから、最後に条件を判定する do-while あるいは rpeat-until を真似るためにこのような形になっています。

4.2. 相加相乗平均

#!/bin/bash

function fmath {
    echo "scale=20; $1" | bc
}

a=1
b=$(fmath "1.0 / sqrt(2.0)")
s=1
t=4

for i in {1..3}
do
    last=$a
    a=$(fmath "($a + $b) / 2.0")
    b=$(fmath "sqrt($last * $b)")
    s=$(fmath "$s - $t * ($a - $last) * ($a - $last)")
    t=$(fmath "$t * 2")
    printf "%16.14f\n" $(fmath "($a + $b) * ($a + $b) / $s")
done

5. Fish

fishのmathは、デフォルトで浮動小数点数の計算を行います。 bcに頼らなくても大丈夫でした。

5.1. Machinの公式

#!/usr/bin/fish

function fmath
    math -s15 $argv
end

set p 0
set k 1
set t (fmath 16.0 / 5.0)

while true
    set last $p
    set p (fmath $p + $t / $k)
    set t (fmath "$t / (-5.0 * 5.0)")
    set k (math $k + 2)
    if test $p -eq $last
        break
    end
end

set k 1
set t (fmath 4.0 / 239.0)

while true
    set last $p
    set p (fmath $p - $t / $k)
    set t (fmath "$t / (-239.0 * 239.0)")
    set k (math $k + 2)
    if test $p -eq $last
        break
    end
end

printf "%.30Lf\n" $p

fishでは do-while あるいは repeat-until をエミュレートする手段がないようです。 代替として、ループの条件をwhileで判定するのではなく、本体の最後でifで判定して、breakするようにしました。

5.2. 相加相乗平均

#!/usr/bin/fish

function fmath
    math -s15 $argv
end

set a 1
set b (fmath "1.0 / (sqrt 2.0)")
set s 1
set t 4

for i in (seq 3)
    set last $a
    set a (fmath "($a + $b) / 2.0")
    set b (fmath sqrt $last x $b)
    set s (fmath "$s - $t * ($a - $last) * ($a - $last)")
    set t (fmath $t x 2)
    printf "%16.14f\n" (fmath "($a + $b) * ($a + $b) / $s")
end

著者: watercat

Created: 2022-09-27 火 21:07

Emacs 28.2 (Org mode 9.5.5)

Validate