Excelで数値計算をしようと思うと、自分で1からそれぞれのプログラムを組む必要があります。
手軽に計算できる方法を調べたところ、ALGLIBというライブラリではExcelでも呼び出しができる数値計算・データ処理ライブラリも提供していました。
導入方法を紹介します。
ALGLIBとは
ALGLIB は、数値計算や最適化アルゴリズムを提供するライブラリで、多言語対応かつ移植性の高い構成が特徴です。特に VBA を含む複数のプログラミング言語で使用可能であり、LAPACK と同様の機能を一部提供しています。
日本語のWikiがありますのでもう少し詳しく知りたい方は、参照してみてください。
ALGLIB の主な特徴
- 多言語対応
- C++、C#、Python、VBA など、さまざまな言語向けに設計されています。VBA 用のインターフェースも用意されており、Excel VBA プロジェクトに組み込みやすいです。
- 機能の幅広さ
ALGLIB は LAPACK に匹敵する数値計算機能を多く備えています。一部の機能例:- 線形代数 (LU 分解、QR 分解、特異値分解など)
- 最適化 (線形最適化、非線形最適化)
- 方程式ソルバー (線形方程式、非線形方程式)
- 固有値計算
- 数値積分
- 補間 (スプライン、ポリノミアル補間)
- DLL の利用で環境依存性が低い
ALGLIB の DLL をプロジェクトに追加するだけで利用可能。インストールが不要で、VBA から簡単に呼び出せます。 - オープンソース & 商用ライセンス
- 無料版 (GPL ライセンス)
- 商用版 (独自ライセンス) が選べます。商用利用を予定していない場合、無料版で十分です。
無料版でも機能は変わらないようですので、大企業での利用や大規模の計算に使わなければ無料版を選択してください。
ALGLIBのダウンロード
こちらのページのからダウンロードできます。
残念ながら、VBA用は旧バージョンの2.6で止まっているようですが、使う分には問題無さそうです。
解凍した\alglib-2.6.0.vb6\vb6\srcにbasファイルを提供してくれています。
必要なbasファイルをExcelにインポートして使います。
主なパッケージ
パッケージ名 | ユニット名 | 日本語の説明 | 対応する .bas ファイル |
---|---|---|---|
DataAnalysis | dforest | 決定木(ランダムフォレスト)分類器 | dforest.bas |
kmeans | K-means++ クラスタリング | kmeans.bas | |
lda | 線形判別分析 | lda.bas | |
linreg | 線形モデル | linreg.bas | |
logit | ロジットモデル | logit.bas | |
mlpbase | 基本的なニューラルネットワーク操作 | mlpbase.bas | |
mlpe | ニューラルネットワークのアンサンブルモデル | mlpe.bas | |
mlptrain | ニューラルネットワークの学習 | mlptrain.bas | |
pca | 主成分分析 | pca.bas | |
DiffEquations | odesolver | 常微分方程式ソルバー | odesolver.bas |
FastTransforms | conv | 高速な実数/複素数の畳み込み演算 | conv.bas |
corr | 高速な実数/複素数の相互相関演算 | corr.bas | |
fft | 実数/複素数のFFT(高速フーリエ変換) | fft.bas | |
fht | 実数の高速ハートレー変換 | fht.bas | |
Integration | autogk | 自動適応型1次元積分 | autogk.bas |
gkq | ガウス-クロード積分公式の生成 | gkq.bas | |
gq | ガウス型積分公式の生成 | gq.bas | |
Interpolation | idwint | 逆距離加重法による補間/フィッティング | idwint.bas |
lsfit | 線形および非線形の最小二乗法 | lsfit.bas | |
polint | 多項式補間/フィッティング | polint.bas | |
pspline | パラメトリックスプライン補間 | pspline.bas | |
ratint | 有理補間/フィッティング | ratint.bas | |
spline1d | 1次元スプライン補間 | spline1d.bas | |
spline2d | 2次元スプライン補間 | spline2d.bas | |
LinAlg | ablas | Level 2およびLevel 3のBLAS演算 | ablas.bas |
bdsvd | 双対化SVD | bdsvd.bas | |
evd | 固有値ソルバー | evd.bas | |
inverseupdate | 行列逆行列のSherman-Morrison更新 | inverseupdate.bas | |
ldlt | LDLT分解 | ldlt.bas | |
matdet | 行列式の計算 | matdet.bas | |
matgen | ランダム行列の生成 | matgen.bas | |
matinv | 行列の逆行列 | matinv.bas | |
ortfac | 実数/複素数のQR, LQ, 二重三重対角化, Hessenberg分解 | ortfac.bas | |
rcond | 条件数の推定 | rcond.bas | |
schur | シュール分解 | schur.bas | |
sdet | 対称行列の行列式 | sdet.bas | |
sinverse | 対称行列の逆行列 | sinverse.bas | |
spdgevd | 一般化対称固有値問題のソルバー | spdgevd.bas | |
srcond | 対称行列の条件数推定 | srcond.bas | |
svd | 特異値分解 | svd.bas | |
trfac | LUおよびコレスキー分解 | trfac.bas |
例題
実数の非対称行列\(A\)の固有値を求めてみます。
$$
A=
\begin{bmatrix}
1& 2& 3\\
0& 4& 5\\
1& 0& 6
\end{bmatrix}
$$
ファイルのインポート
srcフォルダ内の必要なファイルをVBエディター上でインポートします。
- Excel を開き、
Alt + F11
を押して VBA エディタを起動します。 - 「ファイル」→「ファイルのインポート」 を選択。
- 下表のファイルをインポートします。
それぞれの役割は以下の通りです。
ファイル名 | 名称 | 説明 | 関連する関数 |
---|---|---|---|
ap.bas | ALGLIB Primitives? | 基本的なデータ型やユーティリティ関数を含む | Complex 型定義, C_Add , C_Sub など |
blas.bas | Basic Linear Algebra Subprograms? | 線形代数の基本操作を含む | 複素数対応の BLAS 関数 |
evd.bas | Eigenvalue Decomposition? | 固有値分解や複素数を扱う高レベルな関数を含む | HMatrixEVD など |
hsschur.bas | Hessenberg-Schur? | Hessenberg化してSchur 分解 | InternalSchurDecomposition など |
reflections.bas | Reflections? | 行列計算における反射計算 | GenerateReflection など |
rotations.bas | Rotaions? | 行列の回転 | ApplyRotationsFromTheLeftなど |
手間ですが、インポートしたモジュールは名前をModule*から変更しておいた方が良いでしょう。
evd.basモジュールのRMatrixEVD関数を使って計算しますが、この関数から様々な関数を呼び出すため、これら全てのモジュールが必要になります。
RMatrixEVD関数
実行列に対する固有値を求めるには、こちらの関数を使うことになります。
入力パラメータ
A
:- 行列。
- サイズは N×N
- 配列のインデックスは
[0~N-1, 0~N-1]
で指定される。
N
:- 行列
A
のサイズ(行列は正方行列)。
- 行列
VNeeded
:- 固有ベクトルの計算に関するフラグ。
- 値によって計算する内容が変わる:
0
: 固有値のみを計算。1
: 右固有ベクトルを計算。2
: 左固有ベクトルを計算。3
: 左右両方の固有ベクトルを計算。
出力パラメータ
WR
:- 固有値の実数部分。
- 配列
[0..N-1]
に格納される。
WI
:- 固有値の虚数部分。
- 配列
[0..N-1]
に格納される。
VL
:- 左固有ベクトル(必要な場合)。
- 配列
[0..N-1, 0..N-1]
に格納される。
VR
:- 右固有ベクトル(必要な場合)。
- 配列
[0..N-1, 0..N-1]
に格納される。
固有値と固有ベクトルの形式
- 実数固有値の場合:
WI[i] = 0
の場合、固有値は実数。- 固有ベクトルは対応する行列の
i
列に格納される。
- 複素数固有値の場合:
WI[i] > 0
の場合、固有値は次のようにペアで格納される:- \(WR[i] + \sqrt{-1} \cdot WI[i]\)
- \(WR[i+1] + \sqrt{-1} \cdot WI[i+1]\)(これは前者の共役)
- 対応する固有ベクトルは、以下のように格納される:
- 行列の
i
列に固有ベクトルの実部。 - 行列の
i+1
列に固有ベクトルの虚部。
- 行列の
サンプルプログラム
インポートしたモジュール上だと作業しにくいので、Module1(もしくはプロジェクトウインドウの標準モジュールを右クリックし、「挿入」→「標準モジュール」で別のモジュールを追加)にプログラムを記入していきます。
Sub ExampleRMatrixEVD()
Dim A(0 To 2, 0 To 2) As Double
Dim WR() As Double
Dim WI() As Double
Dim VR() As Double
Dim VL() As Double '左固有ベクトルが不要な場合は省略可能
Dim N As Long
Dim VNeeded As Long
Dim I As Long, J As Long
' 行列 A の定義
A(0, 0) = 1: A(0, 1) = 2: A(0, 2) = 3
A(1, 0) = 0: A(1, 1) = 4: A(1, 2) = 5
A(2, 0) = 1: A(2, 1) = 0: A(2, 2) = 6
' パラメータの設定
N = 3
VNeeded = 3 ' 左右固有ベクトル両方を計算
' 固有値と右固有ベクトルを計算
Call RMatrixEVD(A, N, VNeeded, WR, WI, VL, VR)
' 結果の出力
Debug.Print "固有値 (実数部, 虚数部):"
For I = 0 To N - 1
Debug.Print WR(I) & ", " & WI(I)
Next I
Debug.Print "右固有ベクトル:"
For I = 0 To N - 1
For J = 0 To N - 1
Debug.Print "VR(" & I & "," & J & ") = " & VR(I, J)
Next J
Next I
Debug.Print "左固有ベクトル:"
For I = 0 To N - 1
For J = 0 To N - 1
Debug.Print "VL(" & I & "," & J & ") = " & VL(I, J)
Next J
Next I
End Sub
計算結果
イミディエイトウインドウに以下が出力されます。
固有値 (実数部, 虚数部):
7.04096459487746, 0
1.08849668592497, 0
2.87053871919756, 0
右固有ベクトル:
VR(0,0) = 0.633107295508666
VR(0,1) = 1
VR(0,2) = -0.706921069287353
VR(1,0) = 1
VR(1,1) = 0.34965381743329
VR(1,2) = -1
VR(2,0) = 0.608192918975493
VR(2,1) = -0.203603649647201
VR(2,2) = 0.225892256160487
左固有ベクトル:
VL(0,0) = 0.165536477543332
VL(0,1) = 1
VL(0,2) = 0.534605346436768
VL(1,0) = 0.108871032449493
VL(1,1) = -0.686930353241035
VL(1,2) = -0.946655464022549
VL(2,0) = 1
VL(2,1) = 8.84966859249752E-02
VL(2,2) = 1
固有値
$$
\lambda =
\begin{bmatrix}
7.04096459487746\\
1.08849668592497\\
2.87053871919756
\end{bmatrix}
$$
右固有ベクトル
$$
VR =
\begin{bmatrix}
0.633107295508666& 1& -0.706921069287353\\
1& 0.34965381743329& -1\\
0.608192918975493& -0.203603649647201& 0.225892256160487
\end{bmatrix}
$$
左固有ベクトル
$$
VL =
\begin{bmatrix}
0.165536477543332& 1& 0.534605346436768\\
0.108871032449493& -0.686930353241035& -0.946655464022549\\
1& 8.84966859249752E-02& 1
\end{bmatrix}
$$
固有ベクトルは最大値1で正規化されずに出力されているようです。
固有ベクトルは、共役ペアを考慮した形で圧縮されて出力されているので、どれが虚数かを判断するのには慣れが必要になりそうです。なお、今回の例では実数のみで構成された固有ベクトルです。
Numpyとの比較
計算結果を比較するために、PythonでNumpyを使って確認してみます。
Pythonのコード
確認に使ったコードは以下。
import numpy as np
# Define the example matrix
A = np.array([[1, 2, 3],
[0, 4, 5],
[1, 0, 6]])
# Compute eigenvalues and right eigenvectors for A
eigenvalues_A, right_eigenvectors_A = np.linalg.eig(A)
# Compute eigenvalues and right eigenvectors for A^T (transpose of A)
eigenvalues_AT, right_eigenvectors_AT = np.linalg.eig(A.T)
# Display results
print("Original Matrix A:")
print(A)
print("\nEigenvalues of A:")
print(eigenvalues_A)
print("\nRight Eigenvectors of A (columns):")
print(right_eigenvectors_A)
print("\nTranspose of Matrix A (A^T):")
print(A.T)
print("\nEigenvalues of A^T:")
print(eigenvalues_AT)
print("\nRight Eigenvectors of A^T (columns):")
print(right_eigenvectors_AT)
計算結果
Original Matrix A:
[[1 2 3]
[0 4 5]
[1 0 6]]
Eigenvalues of A:
[7.04096459 1.08849669 2.87053872]
Right Eigenvectors of A (columns):
[[ 0.47577536 0.92699459 -0.56767265]
[ 0.75149246 0.3241272 -0.80302126]
[ 0.45705239 -0.18873948 0.18139628]]
Transpose of Matrix A (A^T):
[[1 0 1]
[2 4 0]
[3 5 6]]
Eigenvalues of A^T:
[7.04096459 1.08849669 2.87053872]
Right Eigenvectors of A^T (columns):
[[ 0.16238002 0.82207688 0.36191781]
[ 0.10679508 -0.56470956 -0.64086803]
[ 0.98093197 0.07275108 0.67698129]]
Right Eigenvectors of A^Tが左固有ベクトルに相当します。(行列Aの転置に対する右固有ベクトル)
比較
NumpyとALGLIBでは、出力されている桁数が違うものの答えは同等でした。
固有ベクトルについては、Numpyが大きさ1(ノルム1)で正規化されているのに対し、ALGLIBでは最大値1(もしくは-1)で正規化されているため、見た目の数字としては違いがあります。これは正規化方法が違うだけで、計算結果は同等と言えます。
まとめ
導入が少しだけ面倒なものの、他のライブラリを使うよりは手軽にVBAで計算環境が構築できると思います。
試してみる価値はあると思います。
コメント