PR

【Excel VBA】最小二乗法を用いて多変数関数の多項式近似を行うExcelマクロ

離散化されたデータを最小二乗法を用いて多変数関数の多項式近似を行うExcelマクロについて説明する

↓Excel関数でやりたい人はこちら

スポンサーリンク

最小二乗法の定式化 (Least Square Method)

最小二乗法を用いた多変数関数の多項式近似とは,離散化されたデータに対して最もいい感じにフィットするような多項式の係数を計算する方法である

最小二乗法の行列表現:
主張1: 行列\({\bf A}\)と列ベクトル\({\bf b}\)が与えられたときに
\(||{\bf Ax-b}||\)
を最小にする\({\bf x}\)を求める問題は非常に重要である。
主張2:\({\bf A^t A}\)が正則のとき上記の問題の解は唯一つである:
\({\bf x=(A^T A)^{-1} A^T b}\)

最小二乗法の行列表現(単回帰,多変数,多項式)

今回の例だとこんな感じになる

ここでは,適当に生成した乱数波\(y(x)\)の離散データに対して,6次多項式で近似を行った

\begin{align}
y_{_{LSM}}&=\sum_{i=0}^{6}\left(a_{i}x^{i}\right)\\
&=a_{0}x^{0}+a_{1}x^{1}+a_{2}x^{2}+a_{3}x^{3}+a_{4}x^{4}+a_{5}x^{5}+a_{6}x^{6}
\end{align}

離散データとしては大量の\(x_{0},~x_{1},~\cdots,~x_{6},~y\)の組が与えられる

\(x_{0},~x_{1},~\cdots,~x_{6}\)の組を\({\bf A}\),\(y\)の組を\({\bf b}\),これらにベストフィットする係数\(a_{0},~a_{1},\cdots,~a_{6}\)の組を\({\bf x}\)として, \({\bf x=(A^T A)^{-1} A^T b}\)を計算する

Excelで最小二乗法の解を求める

↓Excelファイルのダウンロードはこちら

シートの構成はこんな感じ

A列からH列の5行目から25行目までに\(x_{0},~x_{1},~\cdots,~x_{6}\)とそれに対応する\(y\)の組が入力されている

(\(x_0\)は定数項なので1が入力されている)

ソースコードはこれ

Sub LeastSquareMethod()
'参考文献:https://mathtrain.jp/leastsquarematrix

'変数の宣言
Dim data1 As Integer: data1 = 21
Dim data2 As Integer: data2 = 7
Dim matrix1() As Double 'A
Dim matrix2() As Double 'At
Dim matrix3() As Double '(At A)
Dim matrix4() As Double '(At A)^-1
Dim matrix5() As Double '(At A)^-1 At
Dim matrix6() As Double 'b
Dim matrix7() As Double '(At A)^-1 At b
Dim output(0, 100) As Double '係数出力用の配列
'カウンター
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim n As Integer
'タイマーの変数
Dim startTime As Double
Dim endTime As Double
Dim processTime As Double

'ActiveWorkbook.Save'まず上書き保存
Application.ScreenUpdating = False '画面更新の非表示
Application.DisplayAlerts = False '警告画面の非表示
startTime = Timer '開始時間取得

ReDim matrix1(data1 - 1, data2 - 1)
ReDim matrix6(data1 - 1, 0)

'多変数関数の配列の作成
For i = 0 To data1 - 1
    For j = 0 To data2 - 1
        matrix1(i, j) = Cells(5 + i, 1 + j) 'x_i
    Next j
    matrix6(i, 0) = Cells(5 + i, 8) 'y
Next i

matrix2 = Transpose(data1, data2, matrix1) 'At
matrix3 = mult(matrix2, matrix1) '(At・A)
matrix4 = inverse(data2, matrix3) '(At A)^-1
matrix5 = mult(matrix4, matrix2) '(At A)^-1 At
matrix7 = mult(matrix5, matrix6) '(At A)^-1 At b
For i = 0 To data2 - 1
    output(0, i) = matrix7(i, 0)
Next i

Range(Cells(2, 1), Cells(2, data2)) = output()

'終了時間取得
endTime = Timer

'処理時間表示
processTime = endTime - startTime
MsgBox "計算が終了しました" & vbCrLf & "処理時間:" & processTime

End Sub
Public Function ans(ByVal n As Integer, ByRef A() As Double, ByRef b() As Double) As Double()
'ガウスの消去法によって,連立方程式Ax=bを解く.
'Aはn×n行列,x,bはn×1行列
'参考文献:「解析塾秘伝!有限要素法のつくり方」 p.82-

'変数の宣言
Dim pivot As Double 'マトリックスの体格成分
Dim p As Double '計算に使用するマトリックスの成分
Dim x() As Double '解を入れる配列
ReDim x(n - 1)

'カウンター
Dim r As Integer
Dim c As Integer
Dim rr As Integer
Dim cc As Integer

'画面更新の非表示
Application.ScreenUpdating = False


'前進消去
For r = 0 To n - 1
    pivot = A(r, r) '対角成分をpivotに代入
    For c = r To n - 1
        A(r, c) = A(r, c) / pivot
    Next c
    b(r) = b(r) / pivot
    For rr = r + 1 To n - 1
        p = A(rr, r)
        For cc = r To n - 1
            A(rr, cc) = A(rr, cc) - p * A(r, cc)
        Next cc
        b(rr) = b(rr) - p * b(r)
    Next rr
Next r

'後退代入
For r = n - 1 To 0 Step -1
    x(r) = b(r)
    For c = r + 1 To n - 1
        x(r) = x(r) - A(r, c) * x(c)
    Next c
Next r

ans = x

End Function
Public Function inverse(ByVal n As Integer, ByRef b() As Double) As Double()
'掃き出し法により逆行列を計算する
'参考資料:http://www.econ.nagoya-cu.ac.jp/~kamiyama/siryou/minv.html

Dim nn As Integer
Dim p As Double 'ピボット
Dim c As Double '行
Dim x As Double
Dim y As Double
Dim A() As Double
Dim Binv() As Double

nn = n + n

ReDim A(n - 1, nn - 1)
ReDim Binv(n - 1, n - 1)

'左半分に逆行列を求めたい行列を入れる
For i = 0 To n - 1
    For j = 0 To n - 1
        A(i, j) = b(i, j)
    Next j
Next i

'行列の右半分に単位行列を入れる
For i = 0 To n - 1
    For j = n To nn - 1
        A(i, j) = 0
    Next j
    A(i, i + n) = 1
Next i

'掃き出し法の基本操作
For k = 0 To n - 1
    p = 0: c = 0
    'k行以降で絶対値が最も大きい行を調べる
    For i = k To n - 1
        If Abs(A(i, k)) > p Then
            p = Abs(A(i, k))
            c = i
        End If
    Next i
    'k行目とc行目の成分を入れ替える
    If c <> k Then
        For j = k To nn - 1
            y = A(k, j)
            A(k, j) = A(c, j)
            A(c, j) = y
        Next j
    End If
    'k行目以降の対角成分をk行目の対角成分で割る
    x = A(k, k)
    For j = k To nn - 1
        A(k, j) = A(k, j) / x
    Next j
    For i = 0 To n - 1
        If i <> k Then
            x = A(i, k)
            For j = k To nn - 1
                A(i, j) = A(i, j) - x * A(k, j)
            Next j
        End If
    Next i
Next k

For i = 0 To n - 1
    For j = n To nn - 1
        Binv(i, j - n) = A(i, j)
    Next j
Next i

inverse = Binv

End Function
Public Function mult(m1() As Double, m2() As Double) As Double()
'm1[l,m]*m2[m,n]の行列の掛け算を行う

Dim l As Integer
Dim m As Integer
Dim n As Integer
Dim sum As Double
Dim ans() As Double

'カウンター
Dim i As Integer
Dim j As Integer
Dim k As Integer

'行列の大きさを取得
l = UBound(m1, 1)
m = UBound(m1, 2)
n = UBound(m2, 2)

'配列の大きさを定義
ReDim ans(l, n)

For i = 0 To l
    For j = 0 To n
    sum = 0
     For k = 0 To m
        sum = sum + m1(i, k) * m2(k, j)
    Next k
    ans(i, j) = sum
    Next j
Next i

mult = ans

End Function
Public Function Transpose(ByVal m As Integer, ByVal n As Integer, ByRef A() As Double) As Double()
'転置行列を計算する
'Aはm×n行列,Atはn×m行列

'変数の宣言
Dim At() As Double '解を入れる配列
ReDim At(n - 1, m - 1) '解を入れる配列

'カウンター
Dim r As Integer
Dim c As Integer

For r = 0 To m - 1
    For c = 0 To n - 1
        At(c, r) = A(r, c)
    Next c
Next r

Transpose = At

End Function

まとめ

離散化されたデータを最小二乗法を用いて多項式近似するExcelマクロについて説明した

今回はn次多項式で説明したが、¥(x_{i}¥)についてはlogでもsinでもexpでもなんでもいい

↓関連記事

コメント