軽量ミニマックス近似式 9 / チェビシェフ補間による近似多項式の係数を補正してミニマックスに近づける

参考: Interface(インターフェース) 2017年 09 月号, pp.109-111
 

import matplotlib.pyplot as plt
import sympy             as sym
import numpy             as np

def newCoeff(c0List, x0List, ε0List):
    dimX = len(x0List)
    dimC = len(c0List)

    Δc = np.array([sym.Symbol("Δc" + str(n)) for n in np.arange(dimC)])
    ε  = sym.Symbol("ε")
    
    # 聯立方程式を解くための行列を組み立てる。
    A = [np.concatenate(([x0List[k]**n for n in np.arange(dimC)],
                         [np.sign(ε0List[k]) for i in range(1+(dimC-1)%2)])) # 正方行列にする手段として、求める必要のない未知項を 1 個または 2 個追加する。
         for k in np.arange(dimX)]
    B = ε0List
    
    # その解を求めて、多項式の係数の補正値だけを抜き出す。
    sol    = np.linalg.solve(A, B)
    ΔcList = np.round(sol[0:dimC], 15)
    
    # その補正値で補正した新たな係数を返す。
    return c0List + ΔcList

########
# test #
########
if __name__ == "__main__":
   
    # 函数 sin(pi * x / 2) を近似することにする。
    x = sym.Symbol("x")
    f = sym.sin(sym.pi * x / 2)

    sinOrigin = lambda x : np.sin(np.pi * x / 2)
    

    # チェビシェフ補間で求めた近似多項式の係数 (0 次 ~ N 次) が下のとおりであったする。
    c0ListOld = np.array([0.0, 1.57065736, -0.0, -0.64345777, 0.0, 0.07293465])
    
    # その近似多項式は下のとおりである。
    dim  = len(c0ListOld)
    gOld_= sum([c0ListOld[n] * x**n for n in np.arange(dim)])
    gOld = lambda x : sum([c0ListOld[n] * x**n for n in np.arange(dim)])
   
    # 元の函数と近似式との誤差が極値を取るときの x の値が下のとおりであったとする。
    x0ListOld = np.array([-1.,-0.8732605,-0.53591919,-0.14245605,0.14245605,0.53591919,0.8732605,1.])
    
    # その x における誤差が下のとおりであったとする。
    ε0ListOld = np.array([1.34240000e-04,-1.17721091e-04,7.14099653e-05,-1.29418676e-05,1.29418676e-05,-7.14099653e-05,1.17721091e-04,-1.34240000e-04])

    # 補正された新たな係数 (0 次 ~ N 次) を求める。
    c0ListNew = newCoeff(c0ListOld, x0ListOld, ε0ListOld)
    print(c0ListNew)
    
    # 新たな近似多項式は下のとおりとなる。
    dim  = len(c0ListNew)
    gNew_= sum([c0ListNew[n] * x**n for n in np.arange(dim)])
    gNew = lambda x : sum([c0ListNew[n] * x**n for n in np.arange(dim)])
    
    
    #"""
    # どれくらいミニマックスに近づいたのかを見てみる。
    x_numbers = np.linspace(-1, 1, 2**8+1)

    sinOrigin = sinOrigin(x_numbers) # 元の sin(pi * x / 2)
    sinCheby  = gOld(x_numbers)      # チェビシェフ補間による近似多項式
    sinMinMax = gNew(x_numbers)      # 1 回だけ係数を補正した多項式
    
    εCheby  = sinOrigin - sinCheby   # チェビシェフ近似との差
    εMinMax = sinOrigin - sinMinMax  # 係数を補正した多項式との差
    
    plt.plot(x_numbers, εCheby)
    plt.plot(x_numbers, εMinMax)
    plt.legend(["εCheby", "εMinMax"])
    plt.show()
    #"""
    
    
    """
    # どれくらいミニマックスに近づいたのかを見てみる。
    εOld = f - gOld_ # これが、チェビシェフ補間による近似式との差
    εNew = f - gNew_ # これが、係数を 1 回だけ補正した近似式との差
    sym.plot(εOld, εNew, (x,-1,1))
    sym.plot(f, gOld, gNew, (x,-2.5,2.5))
    #"""
実行結果 (0 次 ~ N 次の係数を 1 回だけ補正した係数):
[ 0.          1.57031991  0.         -0.64204565  0.          0.07178273]

 
真値と近似式との差は下のとおりである。
1 回補正しただけでほぼミニマックス (誤差の極値の大きさが全部同じである状態) になっている (オレンジ)。
最初のチェビシェフ補間だけで十分な気もする (水色)。
f:id:ti-nspire:20180227164229p:plain:w400