ランベルトのW関数 (The Lambert W function) オメガ関数または対数積 (Product logarithm) としても知られ、 フランスのヨハン・ハインリッヒ・ランベルト(1728年8月26か28日〜1777年9月25日)にちなんで名付けられました。 これは多値関数で、関数 f (w)= w e w の逆関数の分岐です。 ここで、 w は任意の複素数で e w は指数関数です。 ランベルトの W 関数の値が主岐(principal branch)より大きいか -1 に等しいものは、W 0 と表されます。
eW0 — ランベルトの W 関数の主岐(principal branch) の指数cexplambertw() の計算結果の例eW0(x) — tランベルトの W 関数の主岐の指数 — はニュートン・ラフソン法 (Newton-Raphson method)、
を使って求めることができます。また、下記のハレー法 (Halley's method) を使うことも出来ます。。ニュートン・ラフソン法などの反復法を使用して eW0 を求める場合、 特に特異点 -1 / e に非常に近いときに悪い初期値を与えると、収束の観点から酷いことになる —収束が遅いばかりでなく他の枝への跳躍も起こりうる— 恐れがあります。 したがって初期値は十分良い近似となっているばかりでなく、特異点で eW0 に漸近することが望ましいです。 そういった理由で私は初期値を与える近似式として次の式を選びました。 この式は eW0 の x = -1 / e 付近でのピュイズー級数 (Puiseux series) 展開式の第 3項の係数 1 / 3 を0.3で置き換えて第 4項以降を無視したものです。
初期近似 :
eW の z = -1 / e 付近での級数展開 (Puiseux series) :
上記の初期近似式中の係数 0.3 の代わりに、x = 0 のときに式の値がゼロになる (e - √2 - 1) を使うこともできます。
また、ピュイズー級数より、"1 / 3" を使うことも出来ますが、この用途では 0.3 が十分良い値となっています。
次の関係から、ランベルトの W は eW — ランベルトの W 関数の指数を用いて計算できます。
cexplambertw.c (C99) NAME cexplambertw -- exponent of the principal branch of the complex Lambert W function. LIBRARY Math library (libm, -lm) SYNOPSIS #include <math.h> #include <complex.h> double complex cexplambertw(complex double x); RETURN VALUE cexplambertw(x) returns requested exponent of the principal branch of the complex Lambert W function.
Recurrence formula (Halleys method) : Initial approximation :
/* cexplambertw : exponent of Lambert W0 function Rev.1.52 (Dec. 4, 2020) (c) Takayuki HOSODA (aka Lyuka) http://www.finetune.co.jp/~lyuka/technote/lambertw/ Acknowledgments: Thanks to Albert Chan for his informative suggestions. */ #include <math.h> #include <complex.h> #ifdef HAVE_NO_CLOG double complex clog(double complex); double complex clog(double complex x) { return log(cabs(x)) + atan2(cimag(x), creal(x)) * I; } #endif double complex cexplambertw(double complex); double complex cexplambertw(double complex x) { double complex y, p, s, t; double q, r, m; r = 1 / M_E; q = M_E - M_SQRT2 - 1; if ((fabs(creal(x)) <= (DBL_MAX / 1024.0)) && (fabs(cimag(x)) <= (DBL_MAX / 1024.0))) { m = 1.0; s = x + r; if (s == 0) return r; y = (r + csqrt((2.0 * r) * s) + q * s); // approximation near x=-1/e } else { m = (1.0 / 1024.0); x *= m; // scaling s = x * m; y = r * m + csqrt(r * (s * 64.0)) + q * s; y *= 1024.0; } r = DBL_MAX; do { q = r; p = y; t = clog(y); s = 0.5 * (x / y - t * m) / (1 + t) + (1 + t) * m; t = (x - y * (t * m)); y += t / s; r = cabs(y - p); // correction radius } while (r != 0 && q > r);// convergence check by Urabe's theorem return p; }
W0 — ランベルトの W 関数の主枝(principal branch)cexplambertw(1 - i2) = 1.963022024795711 - i1.157904818620495 (i2.220446049250313e-16) cexplambertw(1) = 1.763222834351897 cexplambertw(i) = 1.219531415904638 + i0.7927604805362661 cexplambertw(0) = 1 cexplambertw(-0.36) = 0.4466034047150879 (5.551115123125783e-17) cexplambertw(-0.3678794411714423) = 0.3678794411714423 cexplambertw(-0.37) = 0.3671727690033079 + i0.03950593783033725 (0 - i1.52655665885959e-16) cexplambertw(-1) = 0.168376379087223 + i0.7077541887847276 (-5.551115123125783e-17 + i0) cexplambertw(-1.78) = -0.05991375474182515 + i1.091676160700024 (2.081668171172169e-17 + i0) cexplambertw(-6 + i8) = 0.5264016089780164 + i4.672167782982316 (-1.110223024625157e-16 + i0) cexplambertw(-1e+40 + i1e+40) = -1.105839096727962e+38 + i1.166002733393464e+38 cexplambertw(1e+99) = 4.493356750426822e+96 cexplambertw(1.797693134862316e+308 + i0) = 2.556348163871691e+305 + i0 (-3.89812560456e+289 + i0) cexplambertw(-1.797693134862316e+308 + i0) = -2.556297327179282e+305 + i1.140377281032545e+303 cexplambertw(1.797693134862316e+308 + i1.797693134862316e+308) = 2.557935739687931e+305 + i2.552239351260207e+305 cexplambertw(-1.797693134862316e+308 + i1.797693134862316e+308) = -2.546517666521172e+305 + i2.563606662249022e+305 (3.89812560456e+289 + i0) cexplambertw(0 + i1.797693134862316e+308) = 5.701971348523801e+302 + i2.556335454509411e+305 (7.613526571406249e+286 + i0)
clambertw() の計算結果の例特異点を考慮する必要がありますが、eW の場合はその値がゼロにならないため、 ニュートン・ラフソン法やハレー法で比較的簡単に eW を求めることができました。 しかし、このような反復法で W0 — ランベルトの W の主枝 — を見つけようとすると、 x がゼロになるときに W0もゼロになるため、特異点だけでなくゼロも考慮する必要があります。 言い換えると、反復法の初期値の近似式が、特異点とゼロで漸近的に誤差がゼロになることが強く望まれます。 また、W0 は特異点からの単調増加関数であるため、その近似も単調増加関数である必要があります。 これらの要件を満たすために、初期値は次の近似関数を使用して計算されます。
W0 は特異点の周りに大きな導関数を持っているため、 ニュートン・ラフソン法は、初期近似が不十分な場合、あるいは計算誤差が原因で、 特異点の近くで壊滅的な動作を引き起こす可能性があります。 したがって、W0を得るためには、ニュートン・ラフソン法よりも安定している、 ハレー法あるいは 4次のハウスホルダー法 (Householder's method of order 4) を使用します。
ニュートン・ラフソン法 :
ハレー法 :
4次のハウスホルダー法 :
clambertw.c (C99) NAME clambertw -- the principal branch of the complex Lambert W function. LIBRARY Math library (libm, -lm) SYNOPSIS #include <math.h> #include <complex.h> double complex clambertw(complex double x); RETURN VALUE clambertw(x) returns complex value of the principal branch of the Lambert W function.
Recurrence formula (Halley's method) : Initial approximation :
/* clambertw : Lambert W0 function Rev.1.52 (Dec. 4, 2020) (c) Takayuki HOSODA (aka Lyuka) http://www.finetune.co.jp/~lyuka/technote/lambertw/ Acknowledgments: Thanks to Albert Chan for his informative suggestions. */ #include <math.h> #include <complex.h> #include <float.h> #ifdef HAVE_NO_CLOG double complex clog(double complex); double complex clog(double complex x) { return log(cabs(x)) + atan2(cimag(x), creal(x)) * I; } #endif double complex clambertw(double complex); double complex clambertw(double complex x) { double complex y, p, s, t; double q, r, m; r = 1 / M_E; q = M_E - M_SQRT2 - 1; if ((fabs(creal(x)) <= (DBL_MAX / 1024.0)) && (fabs(cimag(x)) <= (DBL_MAX / 1024.0))) { m = 1.0; s = x + r; if (s == 0) return -1; y = clog(r + csqrt((2.0 * r) * s) + q * s); // approximation near x=-1/e } else { m = (1.0 / 1024.0); x *= m; // scaling s = x * m; y = clog(r * m + csqrt(r * (s * 64.0)) + q * s); y += (M_LN2 * 10.0); } r = DBL_MAX; do { q = r; p = y; t = cexp(y) * m; s = y * t - x; u = (0.5 * y) / (y + 1.0) * s + t + x; y -= s / u;// Halley's method r = cabs(y - p); // correction radius } while (r != 0 && q > r); // convergence check by Urabe's theorem return p; }
WolframAlpha による計算結果の例clambertw(2.718281828459045 + i0) = 0.9999999999999999 + i0 (1.110223024625157e-16 + i0) clambertw(1 - i2) = 0.8237712167092305 - i0.5329289867954415 (0 - i1.110223024625157e-16) clambertw(1 + i0) = 0.5671432904097838 + i0 clambertw(0 + i1) = 0.3746990207371175 + i0.5764127230314353 (-5.551115123125783e-17 + i0) clambertw(0 + i0) = 0 + i0 clambertw(-0.36 + i0) = -0.8060843159708175 + i0 (-2.220446049250313e-16 + i0) clambertw(-0.3678794411714423 + i0) = -1 + i0 clambertw(-0.37 + i0) = -0.996167692712445 + i0.1071826188083488 (3.33066907387547e-16 + i1.956768080901838e-15) clambertw(-1 + i0) = -0.3181315052047642 + i1.337235701430689 (1.110223024625157e-16 + i2.220446049250313e-16) clambertw(-1.78 + i0) = 0.08921804985620939 + i1.625623674427768 (-6.938893903907228e-17 + i0) clambertw(-6 + i8) = 1.547930197079636 + i1.458601930168348 clambertw(-1e+40 + i1e+40) = 87.9726013585729 + i2.329718360883123 clambertw(1e+99 + i0) = 222.5507689557502 + i0 clambertw(1.797693134862316e+308 + i0) = 703.2270331047702 + i0 clambertw(-1.797693134862316e+308 + i0) = 703.2270231685106 + i3.137131632158036 clambertw(0 + i1.797693134862316e+308) = 703.2270306206868 + i1.568565805021136 clambertw(-1.797693134862316e+308 + i1.797693134862316e+308) = 703.5731090989279 + i2.352850357853284 clambertw(1.797693134862316e+308 + i1.797693134862316e+308) = 703.5731140622003 + i0.7842834489371958
gnuplot の splot と pm3d を使った、複素ランベルトの W の 4Dプロットの例 — プロット用スクリプトとデータと参考画像参考文献
ダウンロード : lambertw-gnuplot-pm3d.zip [1MB zip]