M.Hiroi's Home Page

Julia Language Programming

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

[ Home | Light | Julia ]

複素数

近年、多くのプログラミング言語で「複素数 (complex number)」がサポートされるようになりました。たとえば、C言語では 1999 年に発行された規格 C99 で複素数型が導入されました。Go 言語や Python でも複素数型をサポートしていますし、複素数用の標準ライブラリを用意している言語 (C++, Ruby, Haskell など) も多くあります。

他の言語では、FORTRAN や Common Lisp が昔から複素数型をサポートしています。Common Lisp の場合、基本的な数学関数でも複素数を適用できるのであれば、引数に複素数を渡して計算することができます。もちろん、Julia でも複素数を扱うことができます。今回は Julia の複素数についてまとめてみました。

●Julia の数

Julia の数は大きく分けると、整数 (integer)、実数 (float)、有理数 (rational)、複素数 (complex) の 4 種類があります。Julia は BigInt (多倍長整数) をサポートしているので、BigInt を使えばメモリの許す限り任意の精度で扱うことができます。有理数 (分数) は分子と分母を整数で保持していますが、BigInt を使えば任意の精度で扱うことができます。

実数は浮動小数点数 (floating point number) として表現されます。浮動小数点数には IEEE 754 という標準仕様があり、近代的なプログラミング言語のほとんどは、IEEE 754 に準拠した浮動小数点数をサポートしています。浮動小数点数はすべての小数を正確に表現することはできません。このため、実数は近似的 (不正確) な値になります。

IEEE 754 には通常の数値以外にも、負のゼロ (-0.0)、正負の無限大 (∞, -∞)、NaN (Not a Number, 非数) といった値が定義されています。これらの値は Julia でも取り扱うことができます。負のゼロは -0.0、正負の無限大は Inf と -Inf、NaN は NaN として定義されています。

●無限大

一般に、無限大は値のオーバーフロー、ゼロ除算 (数値 / 0.0)、数学関数の計算結果 (たとえば log(0.0)) などで発生します。なお、浮動小数点数のゼロ除算でエラー (例外) を送出する処理系 (たとえば Python など) もあります。

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

julia> Inf
Inf

julia> -Inf
-Inf

julia> 1e308
1.0e308

julia> 1e309
ERROR: syntax: overflow in numeric constant "1e309"

julia> 1e308 * 2
Inf

julia> -1e308
-1.0e308

julia> -1e308 * 2
-Inf

julia> 1.0 / 0.0
Inf

julia> -1.0 / 0.0
-Inf

Julia の場合、無限大の判定は関数 isinf() を使います。

julia> isinf(Inf)
true

julia> isinf(-Inf)
true

julia> isinf(1.234)
false

無限大は他の数値と比較したり演算することもできますが、結果が NaN になることもあります。

julia> Inf == Inf
true

julia> Inf == -Inf
false

julia> -Inf < Inf
true

julia> Inf < -Inf
false

julia> Inf > 0
true

julia> -Inf < 0
true

julia> Inf < 0
false

julia> -Inf > 0
false

julia> Inf + 10
Inf

julia> Inf - 10
Inf

julia> Inf * 10
Inf

julia> Inf / 10
Inf

julia> Inf + Inf
Inf

julia> Inf * Inf
Inf

julia> Inf / Inf
NaN

julia> Inf - Inf
NaN

julia> Inf + -Inf
NaN

julia> Inf * 0.0
NaN

●負のゼロ

負のゼロ (-0.0) は、計算結果が負の極めて小さな値でアンダーフローになったとき発生します。また、正の値を負の無限大で除算する、負の値を正の無限大で除算する、負の値と 0.0 を乗算しても -0.0 が得られます。

julia> -1e-323
-1.0e-323

julia> -1e-324
-0.0

julia> -1e-323 / 2
-5.0e-324

julia> -1e-323 / 4
-0.0

julia> 1.0 / -Inf
-0.0

julia> -1.0 / Inf
-0.0

julia> -1.0 * 0.0
-0.0

標準 (IEEE 754) では、演算子 (Julia では ==) による 0.0 と -0.0 の比較は等しいと判定されます。関数 isequal() を使うと、0.0 と -0.0 を区別することができます。

julia> 0.0 == -0.0
true

julia> -0.0 == 0.0
true

julia> 0.0 != -0.0
false

julia> isequal(0.0, -0.0)
false

julia> isequal(-0.0, -0.0)
true

なお、-0.0 は数学関数 (2 引数の関数 atan [C言語の数学関数 atan2 と同じ] など) や複素数の演算処理などで使われます。

julia> sqrt(0.0)
0.0

julia> sqrt(-0.0)
-0.0

julia> atan(0.0, -1.0)
3.141592653589793

julia> atan(-0.0, -1.0)
-3.141592653589793

●非数

NaN は数ではないことを表す特別な値 (非数) です。一般的には 0.0 / 0.0 といった不正な演算を行うと、その結果は NaN になります。

julia> NaN
NaN

julia> NaN / NaN
NaN

julia> Inf / Inf
NaN

julia> 0.0 / 0.0
NaN

Julia の場合、NaN は関数 isnan() で判別することができます。関数 isfinite は引数が無限大でも NaN でもなければ真を返します。標準 (IEEE 754) では NaN == NaN を偽と判定しますが、isequal(NaN, NaN) は真を返します。

julia> isnan(NaN)
true

julia> isnan(Inf)
false

julia> isnan(1.234)
false

julia> isfinite(NaN)
false

julia> isfinite(Inf)
false

julia> isfinite(1.234)
true

julia> NaN == NaN
false

julia> NaN != NaN
true

julia> isequal(NaN, NaN)
true

●Julia の複素数

数学では複素数 \(z\) を \(x + yi\) と表記します。x を実部、y を虚部、i を虚数単位といいます。虚数単位は 2 乗すると -1 になる数です。実部と虚部の 2 つの数値を格納するデータ構造を用意すれば、プログラミング言語でも複素数を表すことができます。Julia は複素数 \(x + yi\) を x + yim と表記します。実部 x を省略すると x は 0 に、虚部 y を省略して im だけにすると y は 1 になります。

複素数のデータ型名は Complex です。

複素数はコンストラクタ complex(x, y) で生成することもできます。

complex(x, y)

x が実部で、y が虚部を表します。y を省略すると虚部は 0 になります。複素数 z の実部は関数 real で、虚部は関数 imag で取得することができます。複素数の虚部の符号を反転することを「複素共役」といいます。複素共役は関数 conj() で求めることができます。

簡単な例を示しましょう。

julia> 1 + 2im
1 + 2im

julia> typeof(1 + 2im)
Complex{Int64}

julia> 3.0 + 4.0im
3.0 + 4.0im

julia> typeof(3.0 + 4.0im)
ComplexF64 (alias for Complex{Float64})

julia> 2.0im
0.0 + 2.0im

julia> 1.0 + im
1.0 + 1.0im

julia> complex(1.234, 5.678)
1.234 + 5.678im

julia> complex(1.234)
1.234 + 0.0im

julia> a = complex(1.234, 5.678)
1.234 + 5.678im

julia> real(a)
1.234

julia> imag(a)
5.678

julia> conj(a)
1.234 - 5.678im

julia> -1.0im
-0.0 - 1.0im

julia> 1.0 - 0.0im
1.0 - 0.0im

julia> 1.0 + 0.0im
1.0 + 0.0im

julia> -0.0 + 1.0im
0.0 + 1.0im

julia> -0.0 - 1.0im
-0.0 - 1.0im

julia> complex(-0.0, 1.0)
-0.0 + 1.0im

Julia で -1.0im と入力すると、実部は負のゼロになるようです。また、-0.0 + yim (y > 0) のとき、0.0 + yim と解釈されるようです。実部に負のゼロをセットする場合は complex() を使ったほうが良いでしょう。

複素数は極形式 \(z = |z|(\cos \theta + i \sin \theta)\) で表すことができます。このとき、\(|z|\) を絶対値、\(\theta\) を偏角といいます。絶対値は関数 abs() で求めることができます。偏角は複素平面において正の実軸とベクトル (x, y) との角度を表します。偏角を求めるには関数 angle を使います。angle の返り値 \(\theta\) は \(-\pi \leq \theta \leq \pi \) (\(\pi\) : 円周率) です。 このほかに、絶対値の 2 乗を返す関数 abs2() や、絶対値が 1 で偏角が x の複素数を返す関数 cis(x) もあります。cis は cos + i sin の略です。

簡単な例を示しましょう。

julia> a = 1.0 + 1.0im
1.0 + 1.0im

julia> abs(a)
1.4142135623730951

julia> abs2(a)
2.0

julia> angle(a)
0.7853981633974483

julia> pi
π = 3.1415926535897...

julia> pi / 4
0.7853981633974483

julia> cis(pi/4)
0.7071067811865476 + 0.7071067811865475im

julia> angle(1.0 + 0.0im)
0.0

julia> angle(1.0 + 1.0im)
0.7853981633974483

julia> angle(0.0 + 1.0im)
1.5707963267948966

julia> angle(-1.0 + 1.0im)
2.356194490192345

julia> angle(-1.0 + 0.0im)
3.141592653589793

julia> angle(1.0 - 1.0im)
-0.7853981633974483

julia> angle(0.0 - 1.0im)
-1.5707963267948966

julia> angle(-1.0 - 1.0im)
-2.356194490192345

julia> angle(-1.0 - 0.0im)
-3.141592653589793

Julia の場合、-1.0+0.0im の偏角 \(\theta\) は \(\pi\) になり、-1.0-0.0im の偏角は \(-\pi\) になります。ゼロと負のゼロを区別しないプログラミング言語では、偏角 \(\theta\) の範囲を \(-\pi \lt \theta \leq \pi\) に制限して、-1.0 + 0.0im (== -1.0 - 0.0im) の偏角を \(\pi\) とします。

●複素数の四則演算

複素数の四則演算は次のようになります。

\( \begin{array}{l} (a + bi) + (c + di) = (a + c) + (b + d)i \\ (a + bi) - (c + di) = (a - c) + (b - d)i \\ (a + bi) \times (c + di) = (ac - bd) + (bc + ad)i \\ (a + bi) \div (c + di) = \dfrac{ac + bd + (bc - ad)i}{c^2 + d^2} \end{array} \)

これらの演算は Python の関数 +, -, * , / で行うことができます。複素数の場合、大小の比較演算は使えませんが、等値の判定は演算子 == や != で行うことができます。簡単な例を示しましょう。

julia> a = 1.0 + 2.0im
1.0 + 2.0im

julia> b = 3.0 + 4.0im
3.0 + 4.0im

julia> a + b
4.0 + 6.0im

julia> a - b
-2.0 - 2.0im

julia> a * b
-5.0 + 10.0im

julia> a / b
0.44 + 0.08im

julia> a == a
true

julia> a == b
false

julia> a != a
false

julia> a != b
true

実部と虚部の値は Inf, -Inf, NaN になることもあります。

julia> complex(Inf, Inf)
Inf + Inf*im

julia> complex(Inf, NaN)
Inf + NaN*im

julia> NaN + Inf * im
NaN + Inf*im

julia> NaN + NaN * im
NaN + NaN*im

julia> (1e300 + 1e300im) * (1e300 + 1e300im)
NaN + Inf*im

julia> isinf(1+Inf*im)
true

julia> isfinite(1+Inf*im)
false

julia> isfinite(1+2im)
true

julia> isnan(1+Inf*im)
false

julia> isnan(NaN+Inf*im)
true

isinf() は実部または虚部が無限大であれば真を返します。isnan() は実部または虚部が NaN であれば真を返します。isfinite() は実部と虚部が有限な値であれば真を返します。


●複素関数

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

複素数を引数にとる指数関数はオイラー (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}\)

関数 exp と log は複素数に対応しています。べき乗も演算子 ^ で求めることができます。簡単な例を示しましょう。

julia> exp(0.0+pi/4*im)
0.7071067811865476 + 0.7071067811865475im

julia> exp(0.0+pi/2*im)
6.123233995736766e-17 + 1.0im

julia> exp(0.0+pi*im)
-1.0 + 1.2246467991473532e-16im

julia> exp(0.0-pi*im)
-1.0 - 1.2246467991473532e-16im

julia> exp(1.0+1.0im)
1.4686939399158851 + 2.2873552871788423im


julia> log(1.0+1.0im)
0.34657359027997264 + 0.7853981633974483im

julia> log(1.0+0.0im)
0.0 + 0.0im

julia> log(0.0+1.0im)
0.0 + 1.5707963267948966im

julia> log(1.0-1.0im)
0.34657359027997264 - 0.7853981633974483im

julia> log(1e300 + 1e300im)
691.1221014884936 + 0.7853981633974483im


>>> (1.0 + 1.0j) ** 0
(1+0j)
>>> (1.0 + 1.0j) ** 1
(1+1j)
>>> (1.0 + 1.0j) ** 2
2j
>>> (1.0 + 1.0j) ** 3
(-2+2j)
>>> (1.0 + 2.0j) ** (3.0 + 4.0j)
(0.129009594074467+0.03392409290517014j)
>>> (1.0 + 1.0j) ** (1.0 + 1.0j)
(0.2739572538301211+0.5837007587586147j)

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

julia> log(-1.0+0.0im)
0.0 + 3.141592653589793im

julia> log(-1.0-0.0im)
0.0 - 3.141592653589793im

julia> log(-1e300+0.0im)
690.7755278982137 + 3.141592653589793im

julia> log(-1e300-0.0im)
690.7755278982137 - 3.141592653589793im

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

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

プログラミング言語の場合、0.0 と -0.0 を区別する処理系であれば、Julia のように 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 に置き換えた式が三角関数の定義になる

\( \begin{eqnarray} \sin z = \dfrac{e^{iz} - e^{-iz}}{2i} \\ \cos z = \dfrac{e^{iz} + e^{-iz}}{2} \end{eqnarray} \)

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

双曲線関数の定義
\(\begin{eqnarray} \sinh x = \dfrac{e^x - e^{-x}}{2} \\ \cosh x = \dfrac{e^x + e^{-x}}{2} \end{eqnarray}\)
\(\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}\)

Julia の三角関数 (sin, cos, tan) は複素数にも対応しています。簡単な実行例を示します。

julia> a = [0.0+1.0im, 0.0-1.0im, 1.0+1.0im, 1.0-1.0im]
4-element Vector{ComplexF64}:
 0.0 + 1.0im
 0.0 - 1.0im
 1.0 + 1.0im
 1.0 - 1.0im

julia> for x = a println(sin(x)); println(abs(sin(x))) end
0.0 + 1.1752011936438014im
1.1752011936438014
0.0 - 1.1752011936438014im
1.1752011936438014
1.2984575814159773 + 0.6349639147847361im
1.4453965766582495
1.2984575814159773 - 0.6349639147847361im
1.4453965766582495

julia> for x = a println(cos(x)); println(abs(cos(x))) end
1.5430806348152437 - 0.0im
1.5430806348152437
1.5430806348152437 + 0.0im
1.5430806348152437
0.8337300251311491 - 0.9888977057628651im
1.2934544550420957
0.8337300251311491 + 0.9888977057628651im
1.2934544550420957

julia> for x = a println(tan(x)); println(abs(tan(x))) end
0.0 + 0.7615941559557649im
0.7615941559557649
0.0 - 0.7615941559557649im
0.7615941559557649
0.27175258531951174 + 1.0839233273386946im
1.1174700207060704
0.27175258531951174 - 1.0839233273386946im
1.1174700207060704
-- note --------
[*1] 三角関数の公式は引数が複素数でも成り立ちます。ただし、\(|\sin x| \leq 1, |\cos x| \leq 1\) という関係式は、x が実数だと成立しますが複素数では成立しません。

●複素数の双曲線関数

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

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

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 \\ \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}\)

Julia の双曲線関数 (sinh, cosh, tanh) は複素数に対応しています。簡単な使用例を示します。

julia> a
4-element Vector{ComplexF64}:
 0.0 + 1.0im
 0.0 - 1.0im
 1.0 + 1.0im
 1.0 - 1.0im

julia> for x = a println(sinh(x)) end
0.0 + 0.8414709848078965im
0.0 - 0.8414709848078965im
0.6349639147847361 + 1.2984575814159773im
0.6349639147847361 - 1.2984575814159773im

julia> for x = a println(cosh(x)) end
0.5403023058681398 + 0.0im
0.5403023058681398 - 0.0im
0.8337300251311491 + 0.9888977057628651im
0.8337300251311491 - 0.9888977057628651im

julia> for x = a println(tanh(x)) end
0.0 + 1.5574077246549023im
0.0 - 1.5574077246549023im
1.0839233273386946 + 0.27175258531951174im
1.0839233273386946 - 0.27175258531951174im

●複素数の平方根

複素数 \(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) から次の式が導かれます。

三角関数の半角の公式
\(\begin{eqnarray} \sin^2{\left(\frac{\theta}{2}\right)} = \dfrac{1 - \cos \theta}{2} \\ \cos^2{\left(\frac{\theta}{2}\right)} = \dfrac{1 + \cos \theta}{2} \end{eqnarray}\)
\(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}}, \quad (|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}} \)

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

julia> sqrt(1.0 + 0.0im)
1.0 + 0.0im

julia> sqrt(2.0 + 0.0im)
1.4142135623730951 + 0.0im

julia> sqrt(3.0 + 0.0im)
1.7320508075688772 + 0.0im

julia> sqrt(-1.0 + 0.0im)
0.0 + 1.0im

julia> sqrt(-2.0 + 0.0im)
0.0 + 1.4142135623730951im

julia> sqrt(-3.0 + 0.0im)
0.0 + 1.7320508075688772im

julia> a
4-element Vector{ComplexF64}:
 0.0 + 1.0im
 0.0 - 1.0im
 1.0 + 1.0im
 1.0 - 1.0im

julia> for x = a b = sqrt(x); println(b); println(b * b) end
0.7071067811865476 + 0.7071067811865475im
2.220446049250313e-16 + 1.0im
0.7071067811865476 - 0.7071067811865475im
2.220446049250313e-16 - 1.0im
1.09868411346781 + 0.45508986056222733im
1.0000000000000002 + 1.0im
1.09868411346781 - 0.45508986056222733im
1.0000000000000002 - 1.0im

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

julia> sqrt(-2.0 + 0.0im)
0.0 + 1.4142135623730951im

julia> sqrt(-2.0 - 0.0im)
0.0 - 1.4142135623730951im

julia> sqrt(-1e300 + 0.0im)
0.0 + 1.0e150im

julia> sqrt(-1e300 - 0.0im)
0.0 - 1.0e150im

●逆三角関数

三角関数の逆関数を「逆三角関数 (inverse trigonometric function)」といいます。以下に Julia にある逆三角関数を示します。

ここでは引数 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 と表示します。

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

julia> for x = [-1.0, -0.5, 0.0, 0.5, 1.0] println(asin(x)) end
-1.5707963267948966
-0.5235987755982989
0.0
0.5235987755982989
1.5707963267948966

julia> for x = [-1.0, -0.5, 0.0, 0.5, 1.0] println(acos(x)) end
3.141592653589793
2.0943951023931957
1.5707963267948966
1.0471975511965979
0.0

julia> for x = [-1e300, -1.0, 0.0, 1.0, 1e300] println(atan(x)) end
-1.5707963267948966
-0.7853981633974483
0.0
0.7853981633974483
1.5707963267948966

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

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

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

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

julia> for (x, y) = [(-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)] println(atan(y, x)) end
3.141592653589793
2.356194490192345
1.5707963267948966
0.7853981633974483
0.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} \\ 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 は次に示す分枝切断を持っています。

Julia の逆三角関数 (asin, acos, atan) は複素数に対応しています。簡単な実行例を示します。

julia> a
4-element Vector{ComplexF64}:
 0.0 + 1.0im
 0.0 - 1.0im
 1.0 + 1.0im
 1.0 - 1.0im

julia> for x = a b = asin(x); println(b); println(sin(b)) end
0.0 + 0.881373587019543im
0.0 + 1.0im
0.0 - 0.881373587019543im
0.0 - 1.0im
0.6662394324925153 + 1.0612750619050357im
1.0000000000000002 + 1.0im
0.6662394324925153 - 1.0612750619050357im
1.0000000000000002 - 1.0im

julia> asin(2.0+0.0im)
1.5707963267948966 + 1.3169578969248166im

julia> asin(2.0-0.0im)
1.5707963267948966 - 1.3169578969248166im

julia> asin(-4.0+0.0im)
-1.5707963267948966 + 2.0634370688955608im

julia> asin(-4.0-0.0im)
-1.5707963267948966 - 2.0634370688955608im


julia> for x = a b = acos(x); println(b); println(cos(b)) end
1.5707963267948966 - 0.881373587019543im
8.659560562354934e-17 + 1.0im
1.5707963267948966 + 0.881373587019543im
8.659560562354934e-17 - 1.0im
0.9045568943023813 - 1.0612750619050357im
1.0000000000000002 + 1.0im
0.9045568943023813 + 1.0612750619050357im
1.0000000000000002 - 1.0im

julia> acos(2.0+0.0im)
0.0 - 1.3169578969248166im

julia> acos(2.0-0.0im)
0.0 + 1.3169578969248166im

julia> acos(-4.0+0.0im)
3.141592653589793 - 2.0634370688955608im

julia> acos(-4.0-0.0im)
3.141592653589793 + 2.0634370688955608im


julia> for x = a b = atan(x); println(b); println(tan(b)) end
0.0 + Inf*im
0.0 + 1.0im
0.0 - Inf*im
0.0 - 1.0im
1.0172219678978514 + 0.40235947810852507im
1.0 + 1.0im
1.0172219678978514 - 0.40235947810852507im
1.0 - 1.0im

julia> atan(complex(0.0, 2.0))
1.5707963267948966 + 0.5493061443340549im

julia> atan(complex(-0.0, 2.0))
-1.5707963267948966 + 0.5493061443340549im

julia> atan(complex(0.0, -4.0))
1.5707963267948966 - 0.2554128118829953im

julia> atan(complex(-0.0, -4.0))
-1.5707963267948966 - 0.2554128118829953im

atan() の分枝切断において、-0.0 + 2.0im と入力すると 0.0 + 2.0im と解釈されるので、complex() を使って実部に -0.0 を指定しています。

●逆双曲線関数

双曲線関数の逆関数を「逆双曲線関数 (inverse hyperbolic function)」といいます。Julia に用意されている逆双曲線関数には 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}} \)

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

julia> for x = [1.5, 1.0, 0.5, 0.0, -0.5, -1.0, -1.5] b = asinh(x); println(b); println(sinh(b)) end
1.1947632172871094
1.5000000000000002
0.881373587019543
1.0
0.48121182505960347
0.5
0.0
0.0
-0.48121182505960347
-0.5
-0.881373587019543
-1.0
-1.1947632172871094
-1.5000000000000002

julia> for x = [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0] b = acosh(x); println(b); println(cosh(b)) end
0.0
1.0
0.9624236501192069
1.5000000000000002
1.3169578969248166
1.9999999999999998
1.566799236972411
2.5
1.762747174039086
3.0000000000000004
1.9248473002384139
3.5000000000000004
2.0634370688955608
4.000000000000001

julia> for x = [-0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75] b = atanh(x); println(b); println(tanh(b)) end
-0.9729550745276566
-0.75
-0.5493061443340549
-0.5
-0.2554128118829953
-0.24999999999999997
0.0
0.0
0.2554128118829953
0.24999999999999997
0.5493061443340549
0.5
0.9729550745276566
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}\)

参考文献, URL 8 によると、\(\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) \)

\(\sinh^{-1} z, \cosh^{-1} z, \tanh^{-1} z\) は次に示す分枝切断を持っています。

Julia の逆双曲線関数 (asinh, acosh, atanh) は複素数に対応しています。簡単な実行例を示します。

julia> for x = a b = asinh(x); println(b); println(sinh(b)) end
0.0 + 1.5707963267948966im
0.0 + 1.0im
0.0 - 1.5707963267948966im
0.0 - 1.0im
1.0612750619050357 + 0.6662394324925153im
1.0 + 1.0000000000000002im
1.0612750619050357 - 0.6662394324925153im
1.0 - 1.0000000000000002im

julia> asinh(complex(0.0, 2.0))
1.3169578969248166 + 1.5707963267948966im

julia> asinh(complex(-0.0, 2.0))
-1.3169578969248166 + 1.5707963267948966im

julia> asinh(complex(0.0, -4.0))
2.0634370688955608 - 1.5707963267948966im

julia> asinh(complex(-0.0, -4.0))
-2.0634370688955608 - 1.5707963267948966im


julia> for x = a b = acosh(x); println(b); println(cosh(b)) end
0.881373587019543 + 1.5707963267948966im
8.659560562354934e-17 + 1.0im
0.881373587019543 - 1.5707963267948966im
8.659560562354934e-17 - 1.0im
1.0612750619050357 + 0.9045568943023813im
1.0000000000000002 + 1.0im
1.0612750619050357 - 0.9045568943023813im
1.0000000000000002 - 1.0im

julia> acosh(0.0 + 0.0im)
0.0 + 1.5707963267948966im

julia> acosh(0.0 - 0.0im)
0.0 - 1.5707963267948966im

julia> acosh(-4.0 + 0.0im)
2.0634370688955608 + 3.141592653589793im

julia> acosh(-4.0 - 0.0im)
2.0634370688955608 - 3.141592653589793im


julia> for x = a b = atanh(x); println(b); println(tanh(b)) end
0.0 + 0.7853981633974483im
0.0 + 0.9999999999999999im
0.0 - 0.7853981633974483im
0.0 - 0.9999999999999999im
0.40235947810852507 + 1.0172219678978514im
1.0 + 1.0im
0.40235947810852507 - 1.0172219678978514im
1.0 - 1.0im

julia> atanh(2.0 + 0.0im)
0.5493061443340549 + 1.5707963267948966im

julia> atanh(2.0 - 0.0im)
0.5493061443340549 - 1.5707963267948966im

julia> atanh(-4.0 + 0.0im)
-0.2554128118829953 + 1.5707963267948966im

julia> atanh(-4.0 - 0.0im)
-0.2554128118829953 - 1.5707963267948966im

●参考文献, 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 | 雑記帳

Copyright (C) 2021 Makoto Hiroi
All rights reserved.

[ Home | Light | Julia ]