M.Hiroi's Home Page

お気楽C#プログラミング超入門

複素関数


Copyright (C) 2022 Makoto Hiroi
All rights reserved.

●複素数の指数関数と対数関数

複素数を引数にとる指数関数はオイラー (Euler) の公式から導くことができます。

\( \begin{array}{l} e^{i\theta} = \cos \theta + i \sin \theta \quad (Euler's formula) \\ \begin{eqnarray} e^{x+iy} &=& e^x e^{iy} \\ &=& e^x (\cos y + i \sin y) \end{eqnarray} \end{array} \)

複素数の対数関数は複素数 z を絶対値 \(|z|\) と偏角 \(\theta\) を使って導くことができます。

\( \begin{array}{l} x + iy = |z| e^{i\theta} \\ \begin{eqnarray} \log_e (x + iy) &=& log_e |z| e^{i\theta} \\ &=& log_e |z| + log_e e^{i\theta} \\ &=& log_e |z| + i\theta, \quad (-\pi \leq \theta \leq \pi) \end{eqnarray} \end{array} \)

複素数 x, y のべき乗 \(x^y\) は次式で求めることができます。

\(x^y = e^{y\log x}\)

Complex の関数 Exp と Log は複素数に対応しています。べき乗は関数 Pow で求めることができます。簡単な例を示しましょう。

> using System.Numerics;
> var pi = Math.PI;
> pi
3.141592653589793
> Complex.Exp(new Complex(0.0, pi/4))
[<0.7071067811865476; 0.7071067811865475>]
> Complex.Exp(new Complex(0.0, pi/2))
[<6.123233995736766E-17; 1>]
> Complex.Exp(new Complex(0.0, pi))
[<-1; 1.2246467991473532E-16>]
> Complex.Exp(new Complex(1.0, 1.0))
[<1.4686939399158851; 2.2873552871788423>]

> Complex.Log(new Complex(1.0, 1.0))
[<0.3465735902799727; 0.7853981633974483>]
> Complex.Log(new Complex(1.0, 0.0))
[<0; 0>]
> Complex.Log(new Complex(0.0, 1.0))
[<0; 1.5707963267948966>]
> Complex.Log(new Complex(1.0, -1.0))
[<0.3465735902799727; -0.7853981633974483>]
> Complex.Log(new Complex(1e300, 1e300))
[<691.1221014884936; 0.7853981633974483>]

> var a = new Complex(1.0, 1.0);
> a
[<1; 1>]
> Complex.Pow(a, 0)
[<1; 0>]
> Complex.Pow(a, 1)
[<1.0000000000000002; 1>]
> Complex.Pow(a, 2)
[<1.2246467991473535E-16; 2.0000000000000004>]
> Complex.Pow(a, 3)
[<-2.0000000000000004; 2.0000000000000004>]
> Complex.Pow(a, a)
[<0.27395725383012115; 0.5837007587586148>]
> Complex.Pow(new Complex(1, 2), new Complex(3, 4))
[<0.12900959407446702; 0.03392409290517015>]

関数 \(\log z \ (z = x + iy)\) は負の実軸 (\(-\infty \lt x \lt 0\)) において、\(x + 0.0i\) と \(x - 0.0i\) では値が異なります。

> Complex.Log(new Complex(-1.0, 0.0))
[<0; 3.141592653589793>]
> Complex.Log(new Complex(-1.0, -0.0))
[<0; -3.141592653589793>]
> Complex.Log(new Complex(-1e300, 0.0))
[<690.7755278982137; 3.141592653589793>]
> Complex.Log(new Complex(-1e300, -0.0))
[<690.7755278982137; -3.141592653589793>]

このように、関数 log z は負の実軸上で 2 つの値を持ちます。数学では値を一つ返す関数を「一価関数」、複数の値を返す関数を「多価関数」といいます。ここで、定義域を制限することで多価関数を一価関数にみなすことを考えます。関数 log z の場合、負の実軸を定義域から取り除けば、log z を一価関数とみなすことができるわけです。

参考 URL『逆双曲線関数と逆三角関数の branch cut | 雑記帳』によると、この取り除いた領域を branch cut と呼ぶそうです。プログラミングでは branch cut を定義域から取り除くのではなく、その領域では不連続な関数とするそうです。参考文献『COMMON LISP 第 2 版』では「分枝切断線」、Python のドキュメントでは「分枝切断」と記述されています。本稿では branch cut を「分枝切断」と記述することにします。

プログラミング言語の場合、0.0 と -0.0 を区別する処理系であれば、C# のように 2 つの値を区別することができます。0.0 と -0.0 を区別しない処理系では、偏角 \(\theta\) の範囲を \(-\pi \lt \theta \leq \pi\) に制限することで、log z の返り値を (\(-\pi\) を取り除いて) 一つにすることができます。

●複素数の三角関数

複素数の三角関数の定義は、オイラーの公式から導かれる式の \(\theta\) を複素数 \(z\) に変えたものになります。

\(\sin -\theta = -\sin \theta, \ \cos -\theta = \cos \theta \) より
\( \begin{eqnarray} e^{i(-\theta)} &=& \cos -\theta + i \sin -\theta \\ &=& \cos \theta - i \sin \theta \end{eqnarray} \)

\( \begin{array}{l} e^{i\theta} + e^{-i\theta} = 2 \cos \theta \\ \cos \theta = \dfrac{e^{i\theta} + e^{-i\theta}}{2} \\ e^{i\theta} - e^{-i\theta} = 2i \sin \theta \\ \sin \theta = \dfrac{e^{i\theta} - e^{-i\theta}}{2i} \end{array} \)

\(\theta\) を複素数 z に置き換えた式が三角関数の定義になる

\( \sin z = \dfrac{e^{iz} - e^{-iz}}{2i}, \quad \cos z = \dfrac{e^{iz} + e^{-iz}}{2} \)

\(\sin z, \cos z\) に純虚数 \(ix\) を与えると双曲線関数 (\(\sinh x, \cosh x\)) になります。

双曲線関数の定義
\( \sinh x = \dfrac{e^x - e^{-x}}{2}, \quad \cosh x = \dfrac{e^x + e^{-x}}{2} \)
\(\begin{eqnarray} \sin ix &=& \dfrac{e^{iix} - e^{-iix}}{2i} \\ &=& \dfrac{e^{-x} - e^x}{2i} \times \dfrac{-i}{-i} \\ &=& i \dfrac{e^x - e^{-x}}{2} \\ &=& i \sinh x \\ \cos ix &=& \dfrac{e^{iix} + e^{-iix}}{2} \\ &=& \dfrac{e^{-x} + e^x}{2} \\ &=& \cosh x \end{eqnarray}\)

これに三角関数の加法定理 [*1] を使うと次の式が導かれます。

\(\begin{eqnarray} \sin (x + iy) &=& \sin x \cos iy + \cos x \sin iy \\ &=& \sin x \cosh y + i \cos x \sinh y \\ \cos (x + iy) &=& \cos x \cos iy - \sin x \sin iy \\ &=& \cos x \cosh y - i \sin x \sinh y \\ \tan (x + iy) &=& \dfrac{\sin 2x + \sin 2iy}{cos 2x + cos 2iy} \\ &=& \dfrac{\sin 2x + i \sinh 2y}{\cos 2x + \cosh 2y} \end{eqnarray}\)

Complex の三角関数 (Sin, Cos, Tan) は複素数に対応しています。簡単な実行例を示します。

> var xs = new Complex[]{new Complex(0.0,1.0),new Complex(0.0,-1.0),
new Complex(1.0,1.0),new Complex(1.0,-1.0)};
> xs
Complex[4] { [<0; 1>], [<0; -1>], [<1; 1>], [<1; -1>] }

> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Sin(x)); }
(0, 1.1752011936438014)
(0, -1.1752011936438014)
(1.2984575814159773, 0.6349639147847361)
(1.2984575814159773, -0.6349639147847361)
> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Sin(x).Magnitude); }
1.1752011936438014
1.1752011936438014
1.4453965766582497
1.4453965766582497

> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Cos(x)); }
(1.5430806348152437, -0)
(1.5430806348152437, 0)
(0.8337300251311491, -0.9888977057628651)
(0.8337300251311491, 0.9888977057628651)
> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Cos(x).Magnitude);}
1.5430806348152437
1.5430806348152437
1.2934544550420957
1.2934544550420957

> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Tan(x)); }
(0, 0.761594155955765)
(0, -0.7615941559557649)
(0.27175258531951174, 1.0839233273386948)
(0.2717525853195118, -1.0839233273386948)
> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Tan(x).Magnitude); }
0.761594155955765
0.7615941559557649
1.1174700207060706
1.1174700207060706
-- note --------
[*1] 三角関数の公式は引数が複素数でも成り立ちます。ただし、\(|\sin x| \leq 1, |\cos x| \leq 1\) という関係式は、x が実数だと成立しますが複素数では成立しません。

●複素数の双曲線関数

複素数の双曲線関数の定義は、実数の定義で引数 x を複素数 z に変えたものになります。

双曲線関数の定義 (z は複素数)
\( \sinh z = \dfrac{e^{z} - e^{-z}}{2}, \quad \cosh z = \dfrac{e^{z} + e^{-z}}{2} \)

sinh z, cosh z に純虚数 ix を与えると三角関数 (sin x, cos x) になります。

\(\begin{eqnarray} \sinh ix &=& \dfrac{e^{ix} - e^{-ix}}{2} \\ &=& \dfrac{e^{ix} - e^{-ix}}{2} \times \dfrac{i}{i} \\ &=& i \dfrac{e^{ix} - e^{-ix}}{2i} \\ &=& i \sin x \end{eqnarray}\)
\(\begin{eqnarray} \cosh ix &=& \dfrac{e^{ix} + e^{ix}}{2} \\ &=& \cos x \end{eqnarray}\)

これに双曲線関数の加法定理を使うと、次の式が導かれます。

双曲線関数の加法定理
\(\begin{eqnarray} \sinh(x + y) &=& \sinh x \cosh y + \cosh x \sinh y \\ \cosh(x + y) &=& \cosh x \cosh y + \sinh x \sinh y \\ \tanh(x + y) &=& \dfrac{\sinh(x + y)}{\cosh(x + y)} \\ &=& \dfrac{\sinh 2x + \sinh 2y}{\cosh 2x + \cosh 2y} \end{eqnarray}\)
\(\begin{eqnarray} \sinh(x + iy) &=& \sinh x \cos y + i \cosh x \sin y \\ \cosh(x + iy) &=& \cosh x \cos y + i \sinh x \sin y \\ \tanh(x + iy) &=& \dfrac{\sinh(x + iy)}{\cosh(x + iy)} \\ &=& \dfrac{\sinh 2x + i \sin 2y}{\cosh 2x + \cos 2y} \end{eqnarray}\)

Complex の双曲線関数 (Sinh, Cosh, Tanh) は複素数に対応しています。簡単な使用例を示します。

> xs
Complex[4] { [<0; 1>], [<0; -1>], [<1; 1>], [<1; -1>] }

> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Sinh(x)); }
(0, 0.8414709848078965)
(0, -0.8414709848078965)
(0.6349639147847361, 1.2984575814159773)
(0.6349639147847361, -1.2984575814159773)

> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Cosh(x)); }
(0.5403023058681398, 0)
(0.5403023058681398, -0)
(0.8337300251311491, 0.9888977057628651)
(0.8337300251311491, -0.9888977057628651)

> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Tanh(x)); }
(0, 1.557407724654902)
(0, -1.557407724654902)
(1.0839233273386948, 0.27175258531951174)
(1.0839233273386948, -0.27175258531951174)

●複素数の平方根

複素数 z の平方根は次の式で求めることができます。

\(z = x + iy, \ |z| = \sqrt{x^2 + y^2}, \ \theta = \arg z \ (-\pi \leq \theta \leq \pi)\) とすると

\( \sqrt{x + iy} = \begin{cases} \sqrt{|z| e^{i\theta}} = \sqrt{|z|} e^{i\theta/2} & (1) \\ \sqrt{|z| e^{i\theta + 2\pi}} = \sqrt{|z|} e^{i\theta/2 + \pi} & (2) \end{cases} \)

式 (1) を平方根の主値といいます。角度は \(2\pi\) を足すと同じ角度になるので、式 (2) がもう一つの解になります。三角関数の半角の公式を使うと、式 (1) から次の式が導かれます。

三角関数の半角の公式
\( \sin^2{\left(\frac{\theta}{2}\right)} = \dfrac{1 - \cos \theta}{2}, \quad \cos^2{\left(\frac{\theta}{2}\right)} = \dfrac{1 + \cos \theta}{2} \)
\(y \geq 0\) の場合
\(\begin{eqnarray} \sqrt{|z|} e^{i\theta/2} &=& \sqrt{|z|} \left(\cos{\frac{\theta}{2}} + i \sin{\frac{\theta}{2}}\right) = \sqrt{|z|} \left(\sqrt{\frac{1 + \cos \theta}{2}}) + i \sqrt{\frac{1 - \cos \theta}{2}}\right) \\ &=& \dfrac{\sqrt{|z| + |z|\cos \theta}}{2} + i \sqrt{\frac{|z| - |z| \cos \theta}{2}} = \sqrt{\frac{|z| + x}{2}} + i \sqrt{\frac{|z| - x}{2}}, \ (|z|\cos \theta = x) \end{eqnarray}\)

\(y \lt 0\) の場合、虚部の符号が \(-\) になる
\( \sqrt{|z|} e^{i\theta/2} = \sqrt{\dfrac{|z| + x}{2}} - i \sqrt{\dfrac{|z| - x}{2}} \)

Complex の Sqrt は複素数に対応しています。簡単な実行例を示します。

> Complex.Sqrt(Complex.One)
[<1; 0>]
> Complex.Sqrt(Complex.One * 2)
[<1.4142135623730951; 0>]
> Complex.Sqrt(Complex.One * 3)
[<1.7320508075688772; 0>]
> Complex.Sqrt(Complex.One * -1)
[<0; 1>]
> Complex.Sqrt(Complex.One * -2)
[<0; 1.4142135623730951>]
> Complex.Sqrt(Complex.One * -3)
[<0; 1.7320508075688772>]

> xs
Complex[4] { [<0; 1>], [<0; -1>], [<1; 1>], [<1; -1>] }
> foreach (var x in xs) { Console.WriteLine("{0}", Complex.Sqrt(x)); }
(0.7071067811865476, 0.7071067811865475)
(0.7071067811865476, -0.7071067811865475)
(1.09868411346781, 0.45508986056222733)
(1.09868411346781, -0.45508986056222733)

> foreach (var x in xs) {var y = Complex.Sqrt(x); Console.WriteLine("{0}", y * y); }
(2.220446049250313E-16, 1)
(2.220446049250313E-16, -1)
(1.0000000000000002, 1)
(1.0000000000000002, -1)

\(\sqrt x\) は \(\log x\) と同じ分枝切断を持っています。x を負の整数とすると sqrt(x) の解は \(i \sqrt x\) になりますが、もうひとつ \(-i \sqrt x\) という解があります。

> Complex.Sqrt(new Complex(-2.0, 0.0))
[<0; 1.4142135623730951>]
> Complex.Sqrt(new Complex(-2.0, -0.0))
[<0; 1.4142135623730951>]                 // 虚部の符号が逆
> Complex.Sqrt(new Complex(-1e300, 0.0))
[<0; 1E+150>]
> Complex.Sqrt(new Complex(-1e300, -0.0))
[<0; 1E+150>]                             // 虚部の符号が逆

M.Hiroi が使っている .NET 8 では、Sqrt の分枝切断に不具合があるようです。次のように、虚部の符号を判定するとき IsNegative を使うと上手くいきます。

リスト : 複素数の平方根 (修正版)

Complex sqrt(Complex z) {
  var x = z.Real;
  var a = z.Magnitude;
  var b = Math.Sqrt((a - x) / 2);
  if (Double.IsNegative(z.Imaginary)) b = - b;
  return new Complex(Math.Sqrt((a + x) / 2), b);
}
> sqrt(new Complex(-2, 0))
[<0; 1.4142135623730951>]
> sqrt(new Complex(-2, -0.0))
[<0; -1.4142135623730951>]
> sqrt(new Complex(-1e300, 0.0))
[<0; 1E+150>]
> sqrt(new Complex(-1e300, -0.0))
[<0; -1E+150>]

●逆三角関数

三角関数の逆関数を「逆三角関数 (inverse trigonometric function)」といいます。以下に C# のライブラリ Math に用意されている逆三角関数を示します。

ここでは引数 x を実数とします。asin x は引数 x が与えられたとき sin w = x となる角度 w を求めます。同様に acos x は cos w = x となる角度 w を、atan x は tan x = w となる角度 w を求めます。三角関数には周期性があるので、上式を満たす角度 w は無数に存在します。つまり、逆三角関数の返り値は無数にあることになりますが、通常は一つの値を返すように範囲を制限します。これを「主値」といいます。

逆三角関数の主値を以下に示します。

\(\begin{array}{lcc} \arcsin x = w & -1 \leq x \lt 1 & -\pi/2 \leq w \leq \pi/2 \\ \arccos x = w & -1 \leq x \lt 1 & 0 \leq w \leq \pi \\ \arctan x = w & x \in \mathbb{R} & -\pi/2 \leq w \leq \pi/2 \end{array}\)

本ページでは、数式の表示に JavaScript のライブラリ MathJax を使っています。MathJax では、逆三角関数を arcsin, arccos, arctan と表示します。

簡単な実行例を示します。

> foreach (var x in new double[]{-1.0, -0.5, 0.0, 0.5, 1.0}) { Console.WriteLine("{0}", Math.Asin(x)); }
-1.5707963267948966
-0.5235987755982989
0
0.5235987755982989
1.5707963267948966

> foreach (var x in new double[]{-1.0, -0.5, 0.0, 0.5, 1.0}) { Console.WriteLine("{0}", Math.Acos(x)); }
3.141592653589793
2.0943951023931957
1.5707963267948966
1.0471975511965979
0

> foreach (var x in new double[]{-1e300, -1.0, 0.0, 1.0, 1e300}) { Console.WriteLine("{0}", Math.Atan(x)); }
-1.5707963267948966
-0.7853981633974483
0
0.7853981633974483
1.5707963267948966

C# の Math には 2 引数の関数 Atan2(y, x) も用意されています。これは他のプログラミング言語、たとえばC言語の数学関数 atan2 と同じです。

Atan2(double y, double x) => 角度 (ラジアン)

引数 x, y は実数です。Atan2 は直交座標系においてベクトル (x, y) と x 軸との角度を求める関数です。複素平面で考えると、複素数 \(x + iy\) の偏角 \(\theta\) を求めることと同じです。返り値 (角度 \(\theta\)) の範囲は \(-\pi \leq \theta \leq \pi\) になります。

簡単な実行例を示します。

> var ys = new (double,double)[]{(-1.0, 0.0),(-1.0, 1.0),(0.0, 1.0),(1.0, 1.0),
* (1.0, 0.0),(1.0, -1.0),(0.0, -1.0),(-1.0, -1.0),(-1.0, -0.0)};
> foreach(var (x, y) in ys) { Console.WriteLine("{0}", Math.Atan2(y, x)); }
3.141592653589793
2.356194490192345
1.5707963267948966
0.7853981633974483
0
-0.7853981633974483
-1.5707963267948966
-2.356194490192345
-3.141592653589793

●複素数の逆三角関数

複素数の逆三角関数の定義は、複素数の三角関数の定義から導くことができます。\(\arcsin z\) の定義は次のようになります。

\(z, w\) は複素数とする
\(\begin{array}{l} \arcsin z = w \\ z = \sin w = \dfrac{e^{iw} - e^{-iw}}{2i} \end{array}\)
\(\begin{array}{l} z \times 2ie^{iw} = \dfrac{e^{iw} - e^{-iw}}{2i} \times 2ie^{iw} \\ 2iz(e^{iw}) = (e^{iw})^2 - 1 \\ (e^{iw})^2 - 2iz(e^{iw}) - 1 = 0 \end{array}\)
\(e^{iw}\) の二次方程式と考えて解くと

\(e^{iw} = iz \pm \sqrt{1 - z^2}\)

両辺の対数をとって \(-i\) を掛け算し、平方根の主値 \(+\) を選ぶ

\(w = \arcsin z = -i \log{\left(iz + \sqrt{1 - z^2}\right)} \)

\(\arccos z, \arctan z\) は定義だけを示します。

\(\begin{array}{l} \arccos z = \dfrac{\pi}{2} - \arcsin z \\ \arctan z = i \dfrac{\log{\left(1 - iz\right)} - \log{\left(1 + iz\right)}}{2} \end{array}\)

asin, acos, atan は次に示す分枝切断を持っています。

Complex の逆三角関数 (Asin, Acos, Atan) は複素数に対応しています。簡単な実行例を示します。

> xs
Complex[4] { [<0; 1>], [<0; -1>], [<1; 1>], [<1; -1>] }

> foreach(var x in xs){ Console.WriteLine("{0}", Complex.Asin(x)); }
(0, 0.8813735870195429)
(0, -0.8813735870195429)
(0.6662394324925152, 1.0612750619050357)
(0.6662394324925152, -1.0612750619050357)
> foreach(var x in xs){ Console.WriteLine("{0}", Complex.Sin(Complex.Asin(x))); }
(0, 0.9999999999999999)
(0, -0.9999999999999999)
(0.9999999999999999, 1.0000000000000002)
(0.9999999999999999, -1.0000000000000002)

> Complex.Asin(new Complex(2.0, 0.0))
[<1.5707963267948966; 1.3169578969248166>]
> Complex.Asin(new Complex(2.0, -0.0))
[<1.5707963267948966; 1.3169578969248166>]   // 虚部の符号が逆
> Complex.Asin(new Complex(-4.0, 0.0))
[<-1.5707963267948966; 2.0634370688955608>]
> Complex.Asin(new Complex(-4.0, -0.0))
[<-1.5707963267948966; 2.0634370688955608>]  // 虚部の符号が逆

> foreach(var x in xs){ Console.WriteLine("{0}", Complex.Acos(x)); }
(1.5707963267948966, -0.8813735870195429)
(1.5707963267948966, 0.8813735870195429)
(0.9045568943023814, -1.0612750619050357)
(0.9045568943023814, 1.0612750619050357)

> foreach(var x in xs){ Console.WriteLine("{0}", Complex.Cos(Complex.Acos(x))); }
(8.659560562354932E-17, 0.9999999999999999)
(8.659560562354932E-17, -0.9999999999999999)
(0.9999999999999999, 1)
(0.9999999999999999, -1)

> Complex.Acos(new Complex(2.0, 0.0))
[<0; 1.3169578969248166>]                   // 虚部の符号が逆
> Complex.Acos(new Complex(2.0, -0.0))
[<0; 1.3169578969248166>]
> Complex.Acos(new Complex(-4.0, 0.0))
[<3.141592653589793; 2.0634370688955608>]   // 虚部の符号が逆
> Complex.Acos(new Complex(-4.0, -0.0))
[<3.141592653589793; 2.0634370688955608>]

> foreach(var x in xs){ Console.WriteLine("{0}", Complex.Atan(x)); }
(NaN, ∞)
(NaN, -∞)
(1.0172219678978514, 0.4023594781085251)
(1.0172219678978514, -0.4023594781085251)

> foreach(var x in xs){ Console.WriteLine("{0}", Complex.Tan(Complex.Atan(x))); }
(NaN, NaN)
(NaN, NaN)
(0.9999999999999999, 1)
(0.9999999999999999, -1)

> Complex.Atan(new Complex(0.0, 2.0))
[<1.5707963267948966; 0.5493061443340549>]
> Complex.Atan(new Complex(-0.0, 2.0))
[<1.5707963267948966; 0.5493061443340549>]     // 実部の符号が逆
> Complex.Atan(new Complex(0.0, -4.0))
[<-1.5707963267948966; -0.25541281188299525>]  // 実部の符号が逆
> Complex.Atan(new Complex(-0.0, -4.0))
[<-1.5707963267948966; -0.25541281188299525>]

M.Hiroi が使っている .NET 8 では、逆三角関数の分枝切断に不具合があるようです。

●逆双曲線関数

双曲線関数の逆関数を「逆双曲線関数 (inverse hyperbolic function)」といいます。C# の Math に用意されている逆双曲線関数には Asinh, Acosh, Atanh があります。MathJax では逆双曲線関数を \(\sinh^{-1} x, \cosh^{-1} x, \tanh^{-1} x \) で表します。双曲線関数と逆双曲線関数の定義域と値域を示します。

\(\begin{array}{lcc} y = \sinh x & -\infty \lt x \lt \infty & -\infty \lt y \lt \infty \\ y = \cosh x & -\infty \lt x \lt \infty & 1 \leq y \lt \infty \\ y = \tanh x & -\infty \lt x \lt \infty & -1 \lt y \lt 1 \\ y = \sinh^{-1} x & -\infty \lt x \lt \infty & -\infty \lt y \lt \infty \\ y = \cosh^{-1} x & 1 \leq x \lt \infty & 0 \leq y \lt \infty \\ y = \tanh^{-1} x & -1 \lt x \lt 1 & -\infty \lt y \lt \infty \end{array}\)

x と y は実数です。x = cosh y の逆関数 y = acosh x を満たす y の値は 2 つありますが、ここでは \(y \geq 0\) を主値として選ぶことにします。

逆双曲線関数の定義は双曲線関数の定義から導くことができます。

\(\begin{array}{l} \sinh^{-1} x = y \\ y = \sinh x = \dfrac{e^{x} - e^{-x}}{2} \\ y \times 2e^{x} = \dfrac{e^{x} - e^{-x}}{2} \times 2e^{x} \\ 2ye^{x} = e^{2x} - 1 \\ e^{2x} - 2ye^{x} - 1 = 0 \end{array}\)

\(e^{x}\) の 2 次方程式として解く

\(e^{x} = \dfrac{2y \pm \sqrt{4y^2 + 4}}{2} = y \pm \sqrt{y^2 + 1} \)

\(e^{x} \gt 0\) だから平方根の符号に \(+\) を選んで対数をとる

\(x = \log \left(y + \sqrt{y^2 + 1}\right)\)
\(\begin{array}{l} \cosh^{-1} x = y \\ y = \cosh x = \dfrac{e^{x} + e^{-x}}{2} \\ y \times 2e^{x} = \dfrac{e^{x} + e^{-x}}{2} \times 2e^{x} \\ 2ye^{x} = e^{2x} + 1 \\ e^{2x} - 2ye^{x} + 1 = 0 \end{array}\)

\(e^{x}\) の 2 次方程式として解く

\(e^{x} = \dfrac{2y \pm \sqrt{\left(4y^2 - 4\right)}}{2} = y \pm \sqrt{y^2 - 1} \)

\(e^{x} \gt 0\) だから平方根の符号に \(+\) を選んで対数をとる

\(x = \log \left(y + \sqrt{y^2 - 1}\right), \quad (y \geq 1) \)
\(\begin{array}{l} \tanh^{-1} x = y \\ \begin{eqnarray} y &=& \tanh x \\ &=& \dfrac{\sinh x}{\cosh x} \\ &=& \dfrac{e^{x} - e^{-x}}{e^{x} + e^{-x}} \\ &=& \dfrac{e^{2x} - 1}{e^{2x} + 1} \end{eqnarray} \\ y(e^{2x} + 1) = (e^{2x} - 1) \\ (1 - y)e^{2x} = 1 + y \\ e^{2x} = \dfrac{1 + y}{1 - y} \\ x = \dfrac{1}{2} \log{\dfrac{1 + y}{1 - y}} \end{array}\)

関数 \(\tanh^{-1} x\) を使うと、次の関係式から \(\sinh^{-1} x, \ \cosh^{-1} x\) を求めることができます。

\(\sinh^{-1} x = \tanh^{-1}{\dfrac{x}{\sqrt{x^2 + 1}}} \)

\(\cosh^{-1} x = \tanh^{-1}{\dfrac{\sqrt{1 - x^2}}{x}} \)

簡単な実行例を示します。

> foreach (var x in new double[]{1.5, 1.0, 0.5, 0.0, -0.5, -1.0, -1.5}) {
* Console.WriteLine("{0}",Math.Asinh(x));}
1.1947632172871094
0.881373587019543
0.48121182505960347
0
-0.48121182505960347
-0.881373587019543
-1.1947632172871094

> foreach (var x in new double[]{1.5, 1.0, 0.5, 0.0, -0.5, -1.0, -1.5}) {
* Console.WriteLine("{0}",Math.Sinh(Math.Asinh(x))); }
1.5000000000000004
1
0.5
0
-0.5
-1
-1.5000000000000004

> foreach(var x in new double[]{1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0}) {
* Console.WriteLine("{0}",Math.Acosh(x));}
0
0.9624236501192069
1.3169578969248166
1.566799236972411
1.762747174039086
1.9248473002384139
2.0634370688955608

> foreach(var x in new double[]{1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0}){
* Console.WriteLine("{0}", Math.Cosh(Math.Acosh(x))); }
1
1.5
1.9999999999999998
2.5
3.0000000000000004
3.5000000000000004
4.000000000000001

> foreach (var x in new double[]{-0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75}) {
* Console.WriteLine("{0}", Math.Atanh(x)); }
-0.9729550745276566
-0.5493061443340548
-0.25541281188299536
0
0.25541281188299536
0.5493061443340548
0.9729550745276566

> foreach (var x in new double[]{-0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75}) {
* Console.WriteLine("{0}", Math.Tanh(Math.Atanh(x))); }
-0.75
-0.49999999999999994
-0.25
0
0.25
0.49999999999999994
0.75

●複素数の逆双曲線関数

複素数の逆双曲線関数の定義は、実数の定義で引数 x を複素数 z に変えたものになります。

\(\begin{array}{l} \sinh^{-1} z = \log \left(z + \sqrt{z^2 + 1}\right) \\ \begin{eqnarray} \cosh^{-1} z &=& \log \left(z + \sqrt{z^2 - 1}\right) &(1)& \\ &=& \log \left(z + \sqrt{z + 1} \sqrt{z - 1}\right) \quad &(2)& \end{eqnarray} \\ \tanh^{-1} z = \dfrac{1}{2} \log\left(\dfrac{1 + z}{1 - z}\right) = \dfrac{1}{2} \left( \log {\left( 1 + z \right)} - \log {\left(1 - z\right)} \right) \end{array}\)

『逆双曲線関数 - Wikipedia』によると、\(\cosh^{-1} z\) を式 (1) でプログラムすると「分枝切断線」が複雑になるため、他の式 (たとえば (2) など) でプログラムする処理系が多いようです。ちなみに、ANSI Common Lisp では \(\cosh^{-1} z\) を次の式で定義しています。

\( \cosh^{-1} z = 2 \log \left( \sqrt{\dfrac{z + 1}{2}} + \sqrt{\dfrac{z - 1}{2}} \right) \)

asinh, acosh, atanh は次に示す分枝切断を持っています。

Complex には逆双曲線関数 (asinh, acosh, atanh) が定義されていないので、自分で作ることにしましょう。次のリストを見てください。

リスト : 複素数の逆双曲線関数

// 複素数の平方根 (修正版)
Complex sqrt(Complex z) {
  var x = z.Real;
  var a = z.Magnitude;
  var b = Math.Sqrt((a - x) / 2);
  if (Double.IsNegative(z.Imaginary)) b = - b;
  return new Complex(Math.Sqrt((a + x) / 2), b);
}

// 逆双曲線関数
Complex asinh(Complex z) => Complex.Log(z + sqrt(z * z + 1));

Complex acosh(Complex z) {
  var z1 = sqrt(z + 1);
  var z2 = sqrt(z - 1);
  return Complex.Log(z + z1 * z2);
}

Complex atanh(Complex z) => (Complex.Log(1 + z) - Complex.Log(1 - z)) / 2;

修正版の sqrt() を使うだけで、あとは公式をそのままプログラムしただけです。簡単な実行例を示しましょう。

> xs
Complex[4] { [<0; 1>], [<0; -1>], [<1; 1>], [<1; -1>] }
> foreach (var x in xs) { Console.WriteLine("{0}",asinh(x)); }
(0, 1.5707963267948966)
(0, -1.5707963267948966)
(1.0612750619050357, 0.6662394324925153)
(1.0612750619050357, -0.6662394324925153)
> foreach (var x in xs) { Console.WriteLine("{0}",Complex.Sinh(asinh(x))); }
(0, 1)
(0, -1)
(1, 1.0000000000000002)
(1, -1.0000000000000002)

> asinh(new Complex(0.0, 2.0))
[<1.3169578969248166; 1.5707963267948966>]
> asinh(new Complex(-0.0, 2.0))
[<-1.3169578969248166; 1.5707963267948966>]
> asinh(new Complex(0.0, -4.0))
[<2.0634370688955617; -1.5707963267948966>]
> asinh(new Complex(-0.0, -4.0))
[<-2.0634370688955617; -1.5707963267948966>]

> foreach (var x in xs) { Console.WriteLine("{0}",acosh(x)); }
(0.8813735870195429, 1.5707963267948966)
(0.8813735870195429, -1.5707963267948966)
(1.0612750619050357, 0.9045568943023813)
(1.0612750619050357, -0.9045568943023813)
> foreach (var x in xs) { Console.WriteLine("{0}",Complex.Cosh(acosh(x))); }
(8.659560562354932E-17, 0.9999999999999999)
(8.659560562354932E-17, -0.9999999999999999)
(1.0000000000000002, 1)
(1.0000000000000002, -1)

> acosh(new Complex(0.0, 0.0))
[<0; 1.5707963267948966>]
> acosh(new Complex(0.0, -0.0))
[<0; -1.5707963267948966>]
> acosh(new Complex(-4.0, 0.0))
[<2.0634370688955608; 3.141592653589793>]
> acosh(new Complex(-4.0, -0.0))
[<2.0634370688955608; -3.141592653589793>]

> foreach (var x in xs) { Console.WriteLine("{0}",atanh(x)); }
(0, 0.7853981633974483)
(0, -0.7853981633974483)
(0.4023594781085251, 1.0172219678978514)
(0.4023594781085251, -1.0172219678978514)
> foreach (var x in xs) { Console.WriteLine("{0}",Complex.Tanh(atanh(x))); }
(0, 1)
(0, -1)
(1, 0.9999999999999999)
(1, -0.9999999999999999)

> atanh(new Complex(2.0, 0.0))
[<0.5493061443340549; 1.5707963267948966>]
> atanh(new Complex(2.0, -0.0))
[<0.5493061443340549; -1.5707963267948966>]
> atanh(new Complex(-4.0, 0.0))
[<-0.25541281188299525; 1.5707963267948966>]
> atanh(new Complex(-4.0, -0.0))
[<-0.25541281188299525; -1.5707963267948966>]

●参考文献, URL

  1. Guy L. Steele Jr., 『COMMON LISP 第 2 版』, 共立出版, 1991
  2. 奥村晴彦,『C言語による最新アルゴリズム事典』, 技術評論社, 1991
  3. IEEE 754 -- Wikipedia
  4. IEEE 754における負のゼロ - Wikipedia
  5. NaN - Wikipedia
  6. 逆三角関数 - Wikipedia
  7. 逆双曲線関数 - Wikipedia
  8. 逆双曲線関数と逆三角関数の branch cut | 雑記帳

初版 2022 年 2 月 26 日