投げ銭応援よろしくお願いいたします(クリックで投げ銭ページ移動:投げ銭に対する考えも記載あり)。
PR

【主枝W0のみ】ランベルトW関数の計算VBA(標準モジュール)

【主枝W0のみ】ランベルトのW関数(Lambert W function)【VBA】 IT
この記事は約10分で読めます。
2021/9/192021/9/212021/9/22
記事アップ
・タイトル変更
・目次追加
・冒頭部に見出し「概要」追加
・誤記修正(ランベルトのW関数の式の表記)
誤:\(\displaystyle w=L[x]\)
正:\(\displaystyle w=W[x]\)
・プログラムのソースのコメント変更
変更前:Iterate to find Lambert's W
変更後:Lambert W関数の計算処理
・プログラムソースの補足説明の追加
追加内容:PC側でa=0.099999999…や0.100000001…といったような値で解釈されてしまう、ということです。
作品集にbasファイルとxlsmファイルをアップ
(「後日追加予定」の表記削除)

カワッターです。
久々のVBAの投稿です。

今回はHalley法を用いたランベルトのW関数のVBAソースをアップしました。
eのべき乗の逆関数を求めたい方の助けになればと思います。

投げ銭(チップ)の固定ページ:応援よろしくお願いします。
クレジットカード情報はお預かりいたしません。

概要

ランベルトのW関数はeのべき乗の逆関数を求めるために使用します。
具体的には次の方程式の関係の場合に使用できます。

\(\displaystyle x=we^{w}\)

これの逆関数を

\(\displaystyle w=W[x]\)

とすることができ、\(W[x]\)がランベルトのW関数となります。
今回は\(W[x]\)の計算部分をVBAで実装しています。

変数wは複素数です。
ただ今回は実部のみに注目しているため、複素領域\(i\)の部分は出てきません。
この条件の場合、計算する領域は主枝\(\displaystyle W_{0}\)となります。

主枝\(\displaystyle W_{0}\)の範囲はwの値が-1以上です。
グラフのイメージは冒頭のサムネを参照ください。

より詳しい説明は参考文献の章にあるWikipediaのページを参照ください。

ランベルトのW関数は次の記事にあるLTspiceのソーラーパネルの温度特性を再現するためのパラメータを求めるときの副産物としてできました(今はパラメータ計算に使用していません)。
また、まだ予定の段階ですが、作品集のページに標準モジュールとxlsm形式のファイルをアップするつもりです。
→アップしました。

プログラムソース

使用イメージ図を参照ください。
標準モジュールを作成後、ソースをコピペして使用ください。
登録後セルで「=LambertW(セルを指定)」とすれば値が返ります。

ランベルトのW関数_VBA_使用方法
使用イメージ図
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' LambertW function (W0領域のみ)
'
' 機能:引数に入力された値のLambertWの値を返す
'       Halley法を使用
'
' 引数:inputX...LambertW関数のX軸の値
'
' 返り値:
'       0...inputXが0の場合
'      -1...inputXが-1/e±1p(ピコ)以内の場合
'     N/A...inputXが-1/e-1p(ピコ)未満の場合
'       W...Halley法の出力結果
'
'参考ソース:https://blogs.mathworks.com/cleve/2013/09/02/the-lambert-w-function/
'
Option Explicit

Public Function LambertW(inputX As Double)

    'W0領域境界条件用(X=-1/eの時の判定用)
    Dim thresholdValue As Double: thresholdValue = (-1 / Exp(1))
    Dim errValue_ForThreshold_W0 As Double: errValue_ForThreshold_W0 = 1 * 10 ^ (-12) '許容誤差(1p-ピコ)
    
    'Halley法計算用
    Dim W As Double
    Dim tmpE As Double
    Dim tmpFw As Double
    Dim tmpW As Double
    Dim iCount As Long
    Dim isFound As Boolean
    Dim errValue_ForThreshold_Halley As Double: errValue_ForThreshold_Halley = 1 * 10 ^ (-12) '許容誤差(1p-ピコ)


    If inputX = 0 Then '0が入力されたら0を返す
      LambertW = 0
    ElseIf Abs(inputX - thresholdValue) <= errValue_ForThreshold_W0 Then 'inputXが1/e±1p以内であれば-1を返す
      LambertW = -1
    ElseIf inputX < (thresholdValue - errValue_ForThreshold_W0) Then 'inputXが(1/e-1p)未満であればエラーN/A(値見つからず)を返す
      LambertW = CVErr(xlErrNA)
    Else
  
      isFound = False ' 収束フラグ設定
  
      W = 1 '初期値を決定(参考ソースを読み解くと1を代入している)
  
      ' Lambert W関数の計算処理
      For iCount = 1 To 300
  
        'Halley法
        tmpE = Exp(W)
        tmpFw = W * tmpE - inputX
        tmpW = W - tmpFw / ((tmpE * (W + 1) - tmpFw * (W + 2) / (2 * W + 2)))
    
        '誤差の範囲内なら収束フラグをTrue
        If ((Abs(W - tmpW) < errValue_ForThreshold_Halley)) Then
            isFound = True
            Exit For
        Else
            W = tmpW '計算した値を一時保存
        End If
          
      Next iCount
    
    Debug.Assert (isFound) '自分通知用 他の方に使って頂くときなどはコメントアウト
    
    '返り値決定
    LambertW = W
  End If

End Function

補足

関数の型は宣言していません。
関数の返り値にエラー関数CVErr()の値を含めたかったからです。

今回のプログラムでは文字列の場合のエラー処理はいれていません。
そのため引数に文字列で指定しないでください、エラーになります。
必要な場合、分岐処理を適宜追加してください。

引数に入力するinputXの値が-1/eのとき-1になりますが、このとき領域\(W_{0}\)と\(W_{1}\)の閾値となるためプログラムは収束しません。
そのため1/e±1pの範囲内で-1を返すようにしています。

±1pの範囲で判定した理由は次の2点です。

  • ifの判定にdouble型を使用しているから
    double型は浮動小数(2進数による小数点の表現)なので、
    数値同士の計算後、こちらの意図した値が入っていない可能性があります。
    つまりどーゆーことやねん、というと定義の段階で人間側でdouble a=0.1と明示しても、PC側でa=0.099999999…や0.100000001…といったような値で解釈されてしまう、ということです。
  • LTspiceのソーラーパネルの温度特性のパラメータを計算する目的であれば1pの範囲で十分足りるから

型による演算の誤差の扱いは細かいくらいに気を使う必要があります。
詳細はquitaのQ11Q様の記事リンクをつけましたので、参照ください。

VBA VBAでも発生する演算誤差をリテラル文字で解決する - Qiita
#Excelはセルの中の計算でも演算誤差が発生する【悲報】“達人”芳坂和行氏に学ぶ、エクセル「演算誤差」対策講座がPC Onlineから消えていたここで取り扱った一番簡単な演算誤差の発生方法は…

計算が収束したかどうかをはっきりと確認したかったため、収束フラグisFoundにDubug.Assertをつかっています。
Dubug.Assertの説明はこちらの記事が詳しいです。
是非一読ください。
(えくせるちゅんちゅん様の記事です)

Dubug.Assertを使った理由は音が鳴る&VBEが自動で起動するからです。
私はずぼらなので、収束しなかった場合に音がならないと気づかない&何かあったらエディタが立ち上がって欲しいなぁとおもっていました。

記事にも書いてありますが、自分以外の方に使ってもらう場合はDubug.Assertをコメントアウトするなどして無効にした方が良いです。

原理(参考文献)

ランベルトのW関数の原理(参照文献)は次のページを参考にしました。
原理式からVBAにおとし仕込んでいます。

全体を見渡したい参考(Wikipedia)

ランベルトのW関数 - Wikipedia

VBA作成時の参考(Mathworks-MATLABソース)

The Lambert W Function
The Lambert W function deserves to be better known. It pops up in all sorts of places. And our MATLAB function for evalu...

MathworksのHPです。
MATLABのソース(mファイル)があります。
今回はMATLABのソースからVBAに書き直しています。

MATLABのソースは主枝\(W_{1}\)の計算も含めています。
私の場合はLTspiceのパラメータ算出に複素数を使いませんでしたので、主枝\(W_{0}\)のみの領域のみの再現に絞っています。
というのは体の良い言い訳で、正直なところ、まだMATLABソースの主枝\(W_{1}\)に関するソースを読み解けていないです。

余談ですが大学の時はMathmatica、大変お世話になりました。
アームロボットの速度制御のために逆ヤコビアンを求めるときに使っていました。
文字の行列計算が計算過程を含めパパッとされる衝撃は今でも忘れられません。
(ちなみにもう逆ヤコビについては何も思い出せません)

コメント スパム対応をしたつもり、コメントは残す方向で頑張ってます

タイトルとURLをコピーしました