複素数を引数にとる指数関数はオイラー (Euler) の公式から導くことができます。
複素数の対数関数は複素数 z を絶対値 \(|z|\) と偏角 \(\theta\) を使って導くことができます。
複素数 x, y のべき乗 \(x^y\) は次式で求めることができます。
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 z, \cos z\) に純虚数 \(ix\) を与えると双曲線関数 (\(\sinh x, \cosh x\)) になります。
これに三角関数の加法定理 [*1] を使うと次の式が導かれます。
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
複素数の双曲線関数の定義は、実数の定義で引数 x を複素数 z に変えたものになります。
sinh z, cosh z に純虚数 ix を与えると三角関数 (sin x, cos x) になります。
これに双曲線関数の加法定理を使うと、次の式が導かれます。
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 の平方根は次の式で求めることができます。
式 (1) を平方根の主値といいます。角度は \(2\pi\) を足すと同じ角度になるので、式 (2) がもう一つの解になります。三角関数の半角の公式を使うと、式 (1) から次の式が導かれます。
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 は無数に存在します。つまり、逆三角関数の返り値は無数にあることになりますが、通常は一つの値を返すように範囲を制限します。これを「主値」といいます。
逆三角関数の主値を以下に示します。
本ページでは、数式の表示に 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\) の定義は次のようになります。
\(\arccos z, \arctan z\) は定義だけを示します。
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 \) で表します。双曲線関数と逆双曲線関数の定義域と値域を示します。
x と y は実数です。x = cosh y の逆関数 y = acosh x を満たす y の値は 2 つありますが、ここでは \(y \geq 0\) を主値として選ぶことにします。
逆双曲線関数の定義は双曲線関数の定義から導くことができます。
関数 \(\tanh^{-1} x\) を使うと、次の関係式から \(\sinh^{-1} x, \ \cosh^{-1} 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 に変えたものになります。
『逆双曲線関数 - Wikipedia』によると、\(\cosh^{-1} z\) を式 (1) でプログラムすると「分枝切断線」が複雑になるため、他の式 (たとえば (2) など) でプログラムする処理系が多いようです。ちなみに、ANSI Common Lisp では \(\cosh^{-1} z\) を次の式で定義しています。
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>]