Hatena::Diary

L’ArtExplose このページをアンテナに追加 RSSフィード

2000-01-01

[]ニュートン力学

アルツってつらいわ。





力学

  1. 位置、速度、加速度
    • v={dx ¥over dt}
    • a={dv ¥over dt}
    • ¥bold{}v=at+v_0
    • x={1 ¥over 2}at^2+v_0t+x_0
  2. 運動法則
    • ¥sum_i ¥vec{F_i}=¥vec{0} ¥Leftrightarrow a=0(力の釣り合い)
    • ¥bold{}F=ma(慣性)
    • F_{1 ¥Rightarrow 2}+F_{2 ¥Rightarrow 1}=0作用反作用の法則
    • ¥sum_i ¥vec{r_i}¥times¥vec{F_i}=¥vec{0}(天秤の釣り合い)
    • ¥overline{¥vec{R}}={¥sum_i m_i¥vec{r_i} ¥over ¥sum_i m_i}重心位置)
    • ¥overline{¥vec{V}}={¥sum_i m_i¥vec{v_i} ¥over ¥sum_i m_i}(重心速度)
    • ¥overline{¥vec{R}}=¥overline{¥vec{V}}t+¥overline{¥vec{R_0}}(重心の等速運動)
  3. 重力
    • F=G{m_1m_2 ¥over r^2}万有引力の法則)
    • ¥left¥{¥begin{matrix}g&=&G{m ¥over r^2} ¥¥ F&=&mg¥end{matrix}¥right.(重力場)
    • ¥bold{}a=g(重力質量と慣性質量の等価性)
    • ¥left¥{¥begin{matrix} x&=&v_{x0}t+x_0 ¥¥ y&=&{1 ¥over 2}gt^2+v_{y0}t+y_0 ¥end{matrix}¥right.(落下)
    • ¥bold{}v=gt+v_0
    • ¥bold{}E_¥phi=mgh(位置エネルギー)
  4. 等速円運動
    • ¥left¥{¥begin{matrix} x=r ¥cos ¥omega t ¥¥ y=r ¥sin ¥omega t ¥end{matrix}¥right.
    • ¥bold{}v=r¥omega
    • ¥bold{}a=r¥omega^2
    • T={2¥pi ¥over ¥omega}(周期)
    • ¥bold{}F=-kx(バネの力)
    • E={1 ¥over 2}kx^2(バネのエネルギー)
    • ¥omega=¥sqrt{k ¥over m}(単振動の角速度)
    • ¥omega ¥simeq ¥sqrt{l ¥over g}(単振り子の角速度)
  5. 力学的保存量
    • ¥bold{}p=mv=ft力積
    • ¥sum_i {m_iv_i ¥over dt}=0(運動量保存)
    • E_v={1 ¥over 2}mv^2(運動エネルギー)
    • ¥sum_i {E_{vi}+E_{¥phi i} ¥over dt}=0(力学エネルギー保存)
    • E=¥vec{F}¥cdot¥vec{x}=¥begin{vmatrix}F¥end{vmatrix}¥begin{vmatrix}x¥end{vmatrix} ¥cos ¥theta(仕事)
    • P={dE ¥over dt}(仕事率、ワット)




波動


    • 横波: 振動方向と進行方向が直交
    • 縦波: 振動方向と進行方向が同じ
    • ¥bold{}f=T^{-1}(周波数)
    • ¥lambda=vT={v ¥over f}波長
    • ¥bold{}y=A ¥sin (¥omega t - kx)
    • ¥begin{vmatrix}¥vec{x_1}¥end{vmatrix}-¥begin{vmatrix}¥vec{x_2}¥end{vmatrix}=m¥lambda(干渉の振幅最大条件)
    • ¥begin{vmatrix}¥vec{x_1}¥end{vmatrix}-¥begin{vmatrix}¥vec{x_2}¥end{vmatrix}=(m+{1 ¥over 2})¥lambda(干渉の振幅最小条件)
  1. 波の屈折と反射
    • ¥bold{}f_1=f_2(媒体境界での周波数の連続)
    • ¥lambda_2 = {v_2 ¥over v_1} ¥lambda_1(媒体境界での波長の変化)
    • {¥sin ¥theta_1 ¥over ¥sin ¥theta_2}={v_1 ¥over v_2}=n_{12}スネルの法則
    • n_{23}={n_{13} ¥over n_{12}}(相対的な屈折率)
    • 固定端反射: 端で常に振幅0、反射波は位相逆転
    • 開放端反射: 端で常に振幅最大、反射波は位相不変
    • ¥lambda_2={v_s-v_1 ¥over v_s}¥lambda_1(波長のドップラー効果
    • f_2={v_s-v_1 ¥over v_s-v_2}f_1(周波数のドップラー効果)
    • f=¥begin{vmatrix}f_1-f_2¥end{vmatrix}(うなり振動数)
    • ¥lambda_n={2l ¥over n}(弦、両閉、両開気柱の振動モード
    • ¥lambda_n={4l ¥over 2n-1}(片開気柱の振動モード)
    • f ¥propto ¥sqrt{F ¥over ¥rho}(弦の周波数)
    • 屈折率は波長によって変化する
    • 横波のため偏光が存在する
    • n={c ¥over v}(絶対屈折率)
    • ¥bold{}¥sin ¥theta > n_{12}(全反射の条件)
    • ¥bold{}d ¥sin ¥theta = m¥lambda(強めあう回折条件)
    • d ¥sin ¥theta = (m+{1 ¥over 2})¥lambda(弱めあう回折条件)
    • c=3¥times10^8(真空中の光速)



[]相対性理論

[相対性理論]

よくある誤解

さて、一般の人は、相対性理論についてどのように理解しているだろうか。おそらく、多くの人の理解は次のようなものではないか。

天才アインシュタインが、当時、誰も思いつかなかった高度な理論を唐突に発表した。
当時は、誰もが彼の理論を疑ったが、その後、彼が正しいことが証明された。

このような誤解が多いせいか、相対性理論は疑似科学による詐欺行為に悪用されやすい。

しかし、実際の相対性理論は一人の天才によって生み出されたものでも、唐突に発表されたものでもない。過去の理論の蓄積を元に、それらを順当な思考で辻褄の合うように解釈したら、あら不思議、こんな理論が出来ました。・・・が相対性理論真実である。

歴史的経緯

以下に、相対性理論が生まれる歴史的経緯を説明する

 折り合いの悪い物理法則

当時の物理学者は次の物理法則の折り合いの悪さに頭を悩ませていた。何れも法則も観測結果と良く一致し、正しい法則であると信じられていたのに、これらの間には致命的矛盾が生じていた。ただし、相対性原理とニュートン力学の間には何の矛盾もない。これらに矛盾するのは、マクスウェルの方程式である。

相対性原理

今日、何の説明もなく単に相対性原理と言うと相対性理論のことを指す。しかし、ここで挙げる相対性原理とは、ガリレオ・ガリレイの提唱した等速運動系における相対性原理の事である。

相対性理論を含む全ての相対性原理は、どの非加速度運動系でも同じ物理法則が成り立つことを示している。

ガリレイの相対性原理では、異なる非加速度運動系の物体の物理的性質は、ガリレイ変換[1]を行うだけで同じ式で表すことが出来る。

ニュートン力学

ニュートンが発見した力学法則で次の三つの法則から成り立つ。

  • 第一法則=慣性の法則
  • 第二法則=運動方程式
  • 第三法則=作用反作用の法則

これらの法則は、ガリレイの相対性原理が成り立っている。これを別の言い方をすると、ガリレイ不変[2]とも言う。

マクスウェルの方程式

マクスウェルの方程式は、それまでの電気理論と磁気理論を一つに統合した電磁理論の性質を示す連立微積分方程式である。

マクスウェルの方程式は相対性原理と折り合いが悪かった。難しい話となると、相対性原理的に等価なのにマクスウェルの方程式では違う式になる例がある。結果的には同じ値になるのだが、理論上は絶対静止系の存在を想定していることになる。これでは、相対性原理的に今ひとつである。

もっと分かりやすい事例を挙げる。マクスウェルの方程式を解くと、光速は、透磁率と誘電率(何れも物質固有の値)から導かれる定数となる。では、何に対して定数なのだろうか。相対性原理では、どの座標系でも同じ物理法則が成り立つ。相対性原理が成り立つためには、光速が定数となる座標系を特定する必要がある。もし、どんな座標系でも光速が定数になるとなると、相対性原理が成り立たなくなってしまう。

 エーテル仮説

当時の物理学者は、絶対静止系を考えた。そして、光速は絶対静止系に対して定数となるのであって、相対速度が定数になるわけではないとして、物理法則の辻褄合わせを行った。そして、当時有力だった光の波動説と併せて、宇宙はエーテルという未発見の媒質で満たされていて、光はエーテルを伝わる波であり、エーテルが絶対静止系を作っていると考えた。

マイケルソン=モーレーは、絶対静止系と地球の速度差を検出しようとして光速度の等方性干渉実験を行った。当時、地球が太陽の周りを公転していることが知られており、その公転速度も分かっていた。そこで、地球の公転方向(東西)と公転の影響のない方向(南北)の光の伝達時間の差を計れば、絶対静止系と地球の速度差を検出できると考えられた。大凡の光の速度から、必要な精度も計算され、その精度を満足する実験方法が使われた。もし、仮に、ある瞬間、速度差が0となっても、地球は公転しているのだから、別の季節で試せば速度差が観測されるはずであった。ところが、現実に観測された速度差は常に測定限界以下であった。実験時刻や季節を変えて何度実験をしても結果は変わらなかった。つまり、絶対静止系と矛盾する結果が出たのである。

 特殊相対性理論

当時の物理学者は、マイケルソン=モーレーの実験結果に驚いた。そして、エーテル仮説を維持しようと辻褄合わせを行った。その辻褄合わせに、ローレンツ、フィッツジラルド、ポアンカレ等が挑戦した。

そんなある日、アインシュタインは、エーテル仮説に頼らないもっと自然な辻褄合わせを発表した。それが特殊相対性理論である。そこに用いられたローレンツ変換式は、ローレンツが発表した式と全く同じ[3]であるが、エーテル仮説を維持するかどうかの基本的な考え方に違いがあった。

実は、特殊相対性理論の主要部分であるローレンツ変換、加速による質量の増加[4]、光速度不変、光速度=加速限界等は、既に、ローレンツやポアンカレによって発表済みであった。それ故に、特殊相対性理論におけるアインシュタイン独自の功績は少ないと言われる。

 一般相対性理論

当時の物理学者は特殊相対性理論以上のものを求めなかったが、アインシュタインは満足しなかった。なぜなら、特殊相対性理論では等速直線運動[5]しか定式化できなかったからである。それこそが、特殊相対性理論に特殊と付く由縁である。アインシュタインは、あらゆる運動を定式化しようとして一般相対性理論を完成した。

ちなみに、アインシュタインを天才と呼ぶには、少々、数学力に欠けていた。友人グロスマンから数学的手法としてリーマン幾何学を学び、四苦八苦して、アインシュタイン方程式としての定式化に成功したと言う。一般相対性理論の功績の半分はグロスマンにあるとも噂される。

水星の近日点の移動、太陽近傍での恒星の光の屈曲率が一致する等の数々の証拠により、一般相対性理論はほぼ正しいと認められているが、若干の修正の余地があるのではないかとも考えられている。

相対性理論関係年表

ネットのあちらこちらから拾ってきたのでかなり怪しい年表であるが、歴史的経緯を知るには十分であろう。

人物 研究内容
1609 ケプラー ケプラーの第一第二法則を発表
1619 ケプラー ケプラーの第三法則を発表
1632 ガリレオ 等速運動系における相対性原理を発表
1664頃 ニュートン 力学の3法則を見出す
1865 マクスウェル マクスウェルの方程式を発表し電磁波の存在を預言
1887 マイケルソンとモーレー 光速度の等方性干渉実験でエーテル観測されず
1888 ヘルツ 電磁波の実証実験
1891 エートヴェーシュ 重力質量と慣性質量の同一性の実験
1892 ローレンツ ローレンツ短縮仮説の論文を発表
1892 フィッツジェラルド ローレンツ−フィッツジェラルド短縮を発表
1895 ローレンツ ローレンツ短縮仮説の追加論文を発表
1896 マルコーニ 無線電信の実用化
1899 ポアンカレ ポアンカレの相対性原理を発表
1901 カウフマン 高速電子の比電荷(質量)の増加を粗測定(相対性理論とは一致しない)
1902 アブラハム
1904 ローレンツ ローレンツ短縮仮説をローレンツ変換にまとめる
1905 6 30 アインシュタイン 特殊相対性理論の論文を発表
1905 9 27 アインシュタイン 特殊相対性理論の追加論文(E=mc^2)を発表
1907 12 アインシュタイン 一般相対性理論の前段階論文(重力による光の屈曲)を発表
1909 ブーヘラー 高速電子の比電荷(質量)の測定結果が相対性理論と一致
1911 アインシュタイン 一般相対性理論の前段階論文を発表
1912 アルゼンチン隊 皆既日食の観測を計画するが雨で流れる
1914 アインシュタイン 一般相対性理論の前段階論文を発表
1915 アインシュタイン 一般相対性理論の前段階論文(水星の近日点の移動)を発表
1915 11 20 ヒルベルト アインシュタインの論文を元に重力場の方程式を発表
1916 アインシュタイン 一般相対性理論の完成論文を発表
1916 アインシュタイン 一般相対性理論の追加論文(重力波)を発表
1917 アインシュタイン 一般相対性理論の追加論文(宇宙項)を発表
1918 アインシュタイン 一般相対性理論の追加論文を発表
1918 アインシュタイン 一般相対性理論の追加論文(重力波)を発表
1919 5 29 イギリス観測隊 皆既日食時に太陽近傍の恒星の光の屈曲を観測
1919 11 6 イギリス観測隊 観測結果が一般相対性理論と一致したと発表

相対性理論


[]C/C++ スタイルルール

アルツの俺のための俺用メモなんであしからず

とりあえずのスタイルルール

基本的にはその言語開発者の流儀に従う。

C は Brian W. Kernighan and Dennis M. Ritchie, The C Programming Language, second edition (Prentice-Hall, 1988) のスタイル(K&R 流)に従う。

C++ も基本的には上と同じでいいが, Bjarne Stroustrup, The C++ Programming Language, third edition (Addison-Wesley, 1997) に従って char *p; ではなく char* p; と書く。 私の CGI で学ぶ C++コード例参照。

Java も基本的には上と同じでいいが, Java 独自の部分は Sun から出ている Java の本の流儀に従う。 私の Java 言語について のコード例参照。

インデントは K&R の本にならって4桁ごととする。 TAB コードは使わず,スペースで展開する*

* 『C言語による最新アルゴリズム事典』 のソースコードでは VZ エディタで C を編集したときの既定値で TAB を4桁としたが, 環境によって見え方が違ってしまった。 ちなみに,この本は紙を節約するため詰め込みすぎたところがあるが, これは真似る必要はない。

右側のコメントは33バイト目から始める(これはあまり気にする必要はない)。 33という数字は下の indent や CC mode での既定値。

//345678901234567890123456789012345678901234567890
#include <iostream>             // 入出力の準備
using namespace std;            // std を使う
int main(int argc, char* argv[]) // メイン
{
    for (int i = 1; i < argc; i++) // 引数を出力
        cout << argv[i] << '\n';
}
</iostream>

indent を使う

GNUindent を次のように使えば C ソースを(ほぼ)上のルールに則って書き直してくれます:

indent -kr -ts255 *.c

-kr は K&R 流,-ts255 は255文字のスペースをTABコードに置き換える (したがって実際にはTABの代わりにスペースを使う) という意味です。

たとえば次のようなコードを処理してみましょう。

int
foo( double x ){
    if( sin( x ) == 0 )
    {
        return 3;
    }
    else
    {
        return 5;
    }
}

indent -kr -ts255 の出力は次のようになります。

int foo(double x)
{
    if (sin(x) == 0) {
        return 3;
    } else {
        return 5;
    }
}

indent は C++ でも簡単な場合は使えますが,完全ではありません。 Java ではさらに悲惨なことになります。

CC mode を使う

XEmacs/Emacs/Mule ではファイルを開くと拡張子で自動的に言語を判断し, 適当な編集モードになります。 インデントのルールを指定しておけば, 自動的にそれに則ってインデントしてくれますので, スタイルをそろえるのに便利です。 最下行に C とか C++ とか Java とか出るはずですので,確認してください。 実際に働いているのは CC mode という emacs lisp プログラムです。

インデントのルールを K&R 流にするには, ホームディレクトリの .emacs に次のように書き加えます:

(require 'cc-mode)

;; Kernighan & Ritchie スタイルにする
(setq c-default-style "k&r")

;; BackSpace キーを「賢く」し,インデント幅は4桁,タブはスペースに展開
(add-hook 'c-mode-common-hook
     	  '(lambda ()
             (progn
               (c-toggle-hungry-state 1)
               (setq c-basic-offset 4 indent-tabs-mode nil))))

;; .hpp と .h を C++ の拡張子とする(.h の方は余計かも)
(setq auto-mode-alist
      (append
       '(("\\.hpp$" . c++-mode)
         ("\\.h$"   . c++-mode)
         ) auto-mode-alist))

これでも望みのモードにならない場合は,たとえば M-x c++-mode のように打ち込みます。 あるいは,ファイルの先頭に -*- C++ -*- を含む文字列を,たとえば

// This is a -*- C++ -*- header.
のように書いておけば,C++ モードになります。

CC mode のよく使う機能をまとめておきます。

  • TAB でインデントしてくれる
  • C-j で改行してインデントしてくれる
  • M-; でコメントが挿入できる

(付)CC mode のインストール

最新の CC mode のインストールは次のようにします。

まず CC Mode サイトから最新版をいただいてきて,Emacs 用のディレクトリ(下の例では /usr/local/share/emacs/site-lisp)で展開します。

cd /usr/local/share/emacs/site-lisp
tar xvzf .../cc-mode-5.30.8.tar.gz
ln -s cc-mode-5.30.8 cc-mode

Mule (Emacs 19.34) の場合は cc-make.el

(defconst cc-path-to-the-custom-library
  ;; 中略
  nil
  )
となっているところを
(defconst cc-path-to-the-custom-library
  ;; 中略
  "/usr/local/share/emacs/site-lisp/cc-mode/"
  )
に変えます。さらに, custom というライブラリをいただいてきて, 適当なところで展開して Makefile
EMACS   = emacs
EMACS   = mule
にして make してできた *.elc と念のため *.elcc-mode のディレクトリにコピーします。

そうしてから,

mule -batch -no-site-file -q -l cc-make.el cc-*.el
と打ち込みます。

.emacs に上記のパスを設定しておきます。

(setq load-path (cons "/usr/local/share/emacs/site-lisp/cc-mode" load-path))

[]アルゴリズム

アルツが酷いための俺用メモなのであしからず。

整数の平方根と√(x2 + y2) と立方根のアルゴリズム

C言語で書いてあるが、簡単にアセンブリ言語や他言語に移植出来るように書いてあるつもりである。
32bit / 16bit -> 16bit の除算器と 16bit * 16bit -> 32bit の乗算器を持つプロセッサでの利用を想定している。

32bit 整数版 sqrt (平方根)

0x8000 未満の数は 16bit 上位にスケーリングしてから計算している。 ニュートン法を行う前に初期値を近似で求め、ニュートン法を2回行い 1 LSB 誤差が得られた後に、誤差を 1/2 LSB に収めるために LSB を調整している。
平方根をニュートン法で求める場合、誤差は必ず負になるので、1 LSB の誤差が得られた時点で 数値を増やす方向に調整すれば良い。誤差を 1/2 LSB に納めるためには、与えられた整数を x 、計算結果の整数を y として
y2 < x < (y + 1)2 … (1)
となる y が存在し、y に対して誤差が最大の 1/2 LSB となるときの x は
x == (y + 1/2)2
であるが、 x は整数なので
x == y2 + y
のときである。従って、 y2 + y < x の場合に y + 1 を答えにすればよい。
誤差のヒストグラムは 0 〜 231 の全数検査で次のようになる。
err(+1/4..+1/2 LSB) = 536872070
err(-1/4..+1/4 LSB) = 1073739508
err(-1/2..-1/4 LSB) = 536872070
初期値の近似式は次のように1次近似で計算が簡単になるように係数を調整した。
g(x) = (22 * x + 11) / 32 … (2)
(2) 式で約 3% の精度が得られる。
Fig.1 sqrt(x) の近似誤差 (初期値とニュートン法1回目)
sqrt(x)近似誤差

/* NAME
 *    sqrtl - square root function
 * SYNOPSYS
 *    long
 *    sqrtl(long x)
 * DISCRIPTIONS
 *    The sqrtl() function compute the non-negative square root of x.
 * ERROR
 *    Below 1/2 LSB.
 * SEE ALSO
 *    sqrt(3), http://www.finetune.co.jp/~lyuka/fract/sqrt_hypot.html
 * COPYRIGHT
 *    Copyright 2002, Takayuki HOSODA. All rights reserved.
 */
long
_sqrtl(a)
    long a;
{
    register long x;
    register long t;
    register long s;
    int scale;

    x = a;
    if (x > 0) {
        scale = 0;
        if (x < 0x8000) {
            x <<= 16;
            scale = 8;
            a = x;
        }
        x >>= 8;
        s = 8;
        for (t = 0x400000L; x < t; t >>= 2)
            s--;
        t = 88;
        t <<= s;
        x *= 22;
        s += 5;
        x >>= s;                      /* -3.1e-2 < err < +2.9e-2 */

        s = a;
        t += x;
        x = s;
        s /= t;
        s += t;
        s >>= 1;                      /* -4.8e-4 < err <= 0      */
        t = x;
        x /= s;
        x += s;
        x >>= 1;                      /* -1.2e-7 < err <= 0      */
        s = x;
        s++;
        s *= x;
        if (t > s)                    /* adjust LSB              */

            x++;
        if (scale) {
            x += 127;
            x >>= 8;
        }
    }
    return x;
}

16bit 整数版 hypot

ニュートン法を行う前に初期値を近似で求め、ニュートン法を2回行い 1 LSB 誤差が得られた後に、誤差を 1/2 LSB に収めるために LSB を調整している。
初期値の近似式は、1次近似で 1% 以下の精度が得られる次の式 (3) が適している。
g(x) = (53 * x + 129) / 128 … (3)
しかし、ここでは、得られる精度は約 2% だが計算のより簡単な次の式 (4) を採用した。
h(x) = 13 * x / 32 + 1 … (4)
どちらの場合でも 1 LSB 以内の誤差を得るためには2回のニュートン法が必要であるので大差ないからである。
誤差の様子を Fig.2 に示す。
Fig.2 sqrt(1+x) の近似誤差 (初期値とニュートン法1回目)
sqrt(1+x)近似誤差
sqrt のプログラム例と同様に誤差を 1/2 LSB に納めるために LSB を調整している。
/* NAME
 *    hypots  - euclidean distance function
 * SYNOPSYS
 *    unsigned short
 *    hypots(short x, short y)
 * DESCRIPTION
 *     The hypots() function compute the sqrt(x * x + y * y).
 * ERROR
 *    Below 1/2 LSB
 * SEE ALSO
 *    hypot(3),  http://www.finetune.co.jp/~lyuka/fract/hypot.html
 * COPYRIGHT
 *    Copyright 2002, Takayuki HOSODA. All rights reserved.
 */

unsigned short 
_hypots(x, y)
    short x;
    short y;
{
    register unsigned long a;
    register unsigned long b;
    register unsigned long c;
    a = x;
    b = y;
    if(x < 0) a = -x;
    if(y < 0) b = -y;
    c = b;
    if(a != 0) {
        c = a;
        if (b != 0) {
            if(a < b) {
                a = b; b = c;
            }
            b *= b;
            c = b;
            b /= a;
            b *= 13;
            b >>= 5;
            b += a;    /*       0  < error < +1.9e-2 */

            a *= a;
            c += a;
            a = c;     /* apply newton-raphson methode twice */
            a /= b;
            a += b;
            a >>= 1;   /* -1.7e-4 < error <  0       */
            b = c;
            c /= a;
            c += a;
            c >>= 1;   /* -1.3e-8 < error <  0       */

            a = c;
            a++;
            a *= c;
            if (b > a) /* adjust LSB */
                c++;
        }
    }
    return (unsigned short)c;
}

14bit 整数版 hypot

14bit 精度を得るために、初期値の近似式に式 (3) を使用してニュートン法を1回に減らしている。
/* NAME
 *    hypot14  - euclidean distance function
 * SYNOPSYS
 *    short
 *    hypot14(short x, short y)
 * DESCRIPTION
 *     The hypot14() functions compute the sqrt(x * x + y * y).
 * ERROR
 *     Below 1/2 LSB
 * BUGS
 *     Input x, y must be within range of -11,715 to 11,715 for 1/2 LSB error.
 * SEE ALSO
 *    hypot(3),  http://www.finetune.co.jp/~lyuka/fract/hypot.html
 * COPYRIGHT
 *    Copyright 2002, Takayuki HOSODA. All rights reserved.
 */

#include <math.h>

short 
hypot14(short x, short y) {
    short x;
    short y;
{
    register long a;
    register long b;
    register long c;

    a = (long)abs(x);
    b = (long)abs(y);
    c = b;
    if (a != 0) {
        c = a;
        if (b != 0) {
            if(a < b) {
                a = b; b = c;
            }
            b *= b;
            c = b;
            b /= a;
            b *= 53;
            b += a;
            b >>= 7;
            b += a;    /* |err| < 8.5e-3 */
            a *= a;
            c += a;    /* apply newton-raphson methode */

            a = c;
            c /= b;
            c += b;
            c >>= 1;   /* -7.2e-5 < error < 0, 1/sqrt(2^27) = 8.6e-5 */
            b = c;
            b++;
            b *= c;
            if (a > b) /* adjust LSB */
                c++;
        }
    }
    return c;
}


32bit 整数版 cbrt (立方根)

初期値を近似式(5)で求めたあと、漸化式(6)で計算している。
g(x) = (39 * x + 29) / 64 … (5)
近似式(5)は1次近似で計算が簡単になるように係数を調整した。これで約 6% の精度が得られる。
プログラム例の近似部分では x が 1 の時に 近似値が 0 にならないように 1 を加えている。

漸化式(6)は 1次/1次 の Pade 近似をもとにしたもので、3次収束を示す。*Note1
xn+1 = xn * (xn3 + 2 a) / (2 xn3 + a) … (6)
近似と漸化式1回で 1.5e-4 以下の誤差になり、漸化式2回で 2.2e-12 以下の誤差になるが、
32bit 整数の立方根は高々±1,290 (3桁)程度なので漸化式の計算は1回で十分である。
誤差の様子を Fig.3 に示す。
Fig.3 cbrt(x) の近似誤差
cbrt(x)近似誤差
sqrt のプログラム例と同様に誤差を 1/2 LSB に納めるために LSB を調整している。
/* NAME
 *    cbrtl  - cube root function.
 * SYNOPSYS
 *    long
 *    cbrtl(long x)
 * DISCRIPTIONS
 *    The cbrtl() function compute the cube root of x.
 * ERROR
 *    Below 1/2 LSB.
 * SEE ALSO
 *    cbrt(3), http://www.finetune.co.jp/~lyuka/fract/sqrt_hypot.html
 * COPYRIGHT
 *    Copyright 2003, Takayuki HOSODA. All rights reserved.
 */
long
cbrtl(a)
    long a;
{
    long x, t, b;

    if (a == 0)
        return 0;
    if (a == 0x80000000L)
        return -1290;
    x = b = abs(a);
    for (t = 1; x > t; t <<= 1)
        x >>= 2;
    x = ((x * 39 + t * 29) >> 6) + 1;       /* -0.06 < err < 0.06 */
    t = b / (x * x);
    x = (x * (t + t + x)) / (x + x + t);    /* |err| < 1.5e-4 */

    t = x * x;
    if ((6 * t + 3 * x) < (b - x * t) * 4)
        x++;                                /* adjust LSB */
    if (a < 0)
        return -x;
    return x;
}

Note1: 一般に a の n 乗根(但し n≒0) の 3次収束の漸化式は
 x' = x * ((n - 1) * xn + (n + 1) * a) / ((n + 1) * xn + (n - 1) * a) 

で与えられる。

[]ニュートン法

俺用メモなんであんま気にしないでください。

平方根

同様の手法が平方根の計算にも適用できる。平方根 √A のニュートン法による漸化式は
Xn+1 = (Xn + A / Xn) / 2
で与えられるが、この式には A / Xn の除算を行わなければならないという不利な点がある。
そこで、1 / √A のニュートン法を考えてみると、より良い漸化式が得られることに気がつく。

1 / √A のニュートン法による漸化式
Xn+1 = Xn * (3 - A * Xn2) / 2
つまり、上式は(3/2, 1/2 は定数であるので)、乗算が必要とされるだけである。
√A を求めるには、 B = 1 / √A を求めてから、√A = A * B により √A を求める。
逆数の計算の場合と同様に高次収束の漸化式を利用するのが良い。ここでは、1 / √(1 - h) の級数展開を使用している。

1 / √A の 3次収束の漸化式
hn = 1 - A * xn2
xn+1 = xn * (1 + hn * (4 + 3 * hn) / 8)

hn は、xn の誤差と同じである。繰り返しごとに有効桁は3倍になる。
1 / √A の 4次収束の漸化式
hn = 1 - A * xn2
xn+1 = xn * (1 + hn * (8 + hn * (6 + 5 * hn)) / 16)
1 / √A の 5次収束の漸化式
hn = 1 - A * xn2
xn+1 = xn * (1 + (64 * hn + hn2 * (48 + 40 * hn + 35 * hn2)) / 128)
1 / √A の 6次収束の漸化式
hn = 1 - A * xn2
xn+1 = xn * (1 + hn * (1/2 + hn * (3/8 + hn * (5/16 + hn * (35/128 + hn * 63/256)))))

1 / √A の場合と同様に、√(1 - h) の級数展開を使用している。

√A の6次収束の漸化式
hn = 1 - A / xn2
xn+1 = xn * (1 - hn * (1/2 + hn * (1/8 + hn * (1/16 + hn * (5/128 + hn * 7/256)))))

1 / 3√A の6次収束の漸化式
hn = 1 - A * xn3
xn+1 = xn + xn * hn * (1/3 + hn * (2/9 + hn * (14/81 + hn * (35/243 + hn * 91/729))))

1 / 4√A の6次収束の漸化式
hn = 1 - A * xn4
xn+1 = xn + xn * hn * (1/4 + hn * (5/32 + hn * (15/128 + hn * (195/2048 + hn * 663/8192))))

4次収束の √x のCプログラム例
#include <stdio.h>
#include <math.h>
#ifndef DBL_EPSILON
#define DBL_EPSILON     2.2204460492503131E-16
#endif

double
_sqrtinv(a)
    double a;
{
    double x, h, g;
    int    e;

    /* enhalf the exponent for a half digit initial accuracy */
    frexp(a, &e);
    x = ldexp(1.0, -e >> 1);

    /* 1/sqrt(a) 4th order convergence */
    g = 1.0;
    while(fabs( h = 1.0 - a * x * x) < fabs(g)) {
        x += x * (h * (8.0 + h * (6.0 + 5.0 * h)) / 16.0);
        g = h;
    }
    return(x);
}

double
sqrt(a)
    double(a);
{
    if(a < 0){
        fprintf(stderr, "sqrt: domain error\n");
        return(0);
    }
    return(a * _sqrtinv(a));
}

[]収束公式-GaussLegendre-

俺用メモなのであしからず。
【ガウス・ルジャンドル(Gauss-Legendre)の2次の収束公式によるプログラム例】
very_long_float
pi2(iter)
    int             iter;
{
    int             i;
    very_long_float a, b, t, x, y;

    a = 1;
    b = 1 / sqrt(2);
    t = 1;
    x = 4;
    for (i = 0; i < iter; i++) {
        y = a;
        a = (a + b) / 2;
        b = sqrt(y * b);
        t -= x * sqr(y - a);
        x *= 2;
    }
    return (sqr(a + b) / t);
}
(但し、sqr(x) は x2, pow4(x) は x4, sqrt(x) は x1/2, qroot(x) は x1/4)
この公式は2次の収束を示し、n桁の答えが log2(n) 回程度の反復で求まる。
繰り返しごとの誤差は次のようになる。
err( 1) = -3.2257622e-4
err( 2) = -2.3479336e-9
err( 3) = -5.8292283e-20
err( 4) = -1.7418264e-41
err( 5) = -7.6589246e-85
err( 6) = -7.3484406e-172
err( 7) = -3.3697129e-346
err( 8) = -3.5362794e-695
err( 9) = -1.9454428e-1393
err(10) = -2.9425892e-2790

【ボールウェイン(P.Borwein)の4次の収束公式によるプログラム例】
very_long_float
pi(iter)
    int             iter;
{
    int             i;
    very_long_float a, b, y, t;

    y = sqrt(2) - 1;
    a = 6 - sqrt(32);
    b = 8;
    for (i = 0; i < iter; i++) {
        t = qroot(1 - pow4(y));
        y = (1 - t) / (1 + t);
        t = sqr(1 + y);
        a = sqr(t) * a - y * (t - y) * b;
        b *= 4;
    }
    return (1 / a);
}
この公式は4次の収束を示し、繰り返しごとの誤差は次のようになる。
err(1) = -2.3479336e-9
err(2) = -1.7418264e-41
err(3) = -7.3484406e-172
err(4) = -3.5362794e-695
err(5) = -2.9425892e-2790

おまけ。ニュートン法的な Beeler (1972) の公式
very_long_float h;
very_long_float a0 = 355.0/113.0;


/* ai+1 = ai + sin(ai) は π に収束する。
 * ai+1 = ai - tan(ai) でも同じ。
 */ 

while(abs(h = sin(a)) > epsilon) {
    a += h;
}
この公式は3次の収束を示し、繰り返しごとの誤差は次のようになる。
err(1) = 1.007e-21
err(2) = 1.680e-63
err(3) = 7.804e-189