8.4 帕德逼近

8.4 帕德逼近讲述了帕德逼近的原理 并且给出了测试通过的 python 代码

大家好,欢迎来到IT知识分享网。

简介

  帕德逼近Padé approximant是一种对任意函数的有理函数逼近。这个是高中数学内容,经常在高考题中出现,但是呢,很多人忘掉了,以为是高等数学内容。有理函数不消我多说了吧,是分子分母都是多项式的函数。帕德逼近有阶的概念,如果分子是m阶多项式,分母是n阶多项式,那么帕德逼近就是 m / n m/n m/n阶的帕德逼近,符号为 [ m / n ] [m/n] [m/n]或者 R m , n R_{m,n} Rm,n。当n=0的时候,就是多项似乎逼近了。多项式逼近有多种,常见的有切比雪夫逼近和泰勒级数,帕德逼近在n=0时,是泰勒级数的前m项。
  帕德逼近还有个小细节,它的基本形式如下:
[ m / n ] = R m , n = p 0 + p 1 x + p 2 x 2 + ⋯ + p m x m 1 + q 1 x + q 2 x 2 + ⋯ + q n x n [m/n]=R_{m,n}=\frac{p_0+p_1x+p_2x^2+\dots+p_mx^m}{1+q_1x+q_2x^2+\dots+q_nx^n} [m/n]=Rm,n=1+q1x+q2x2++qnxnp0+p1x+p2x2++pmxm
  这个细节就是分母是以1开始的,也就是 q 0 = 1 q_0=1 q0=1
  与切比雪夫近似值不同的是,帕德逼近没有限定定义域,也就是在目标函数的整个定义域内有唯一的帕德逼近。而切比雪夫近似值是目标函数在不同的定义域内有不同的切比雪夫近似值。帕德逼近是以0为基础的,在 x = 0 x=0 x=0点的误差最小,离0越远,误差越大。
  讲了这么多,帕德逼近怎么求呢?切比雪夫近似值的求法是很麻烦的,要用到复杂的雷米兹算法,但是帕德逼近的求法很简单。我拿个例子来说吧。




例子

  以求 f ( x ) = s i n ( x ) f(x)=sin(x) f(x)=sin(x) [ 2 / 2 ] [2/2] [2/2]阶帕德逼近为例子,我讲讲帕德逼近的过程。因为分母有2阶,而分子也有2阶,所以总体复杂度有4阶。而 s i n ( x ) sin(x) sin(x)是一个很“无理”的函数,所以要让他“有理”。那么有两种办法,第一种办法是泰勒级数,第二种办法是切比雪夫近似值。因为切比雪夫近似值需要有定义域,所以直接排除,那么只剩下泰勒级数了。那么我们就展开到4阶吧。
s i n ( x ) = x − x 3 3 ! + O ( x 5 ) sin(x) = x-\frac{x^3}{3!}+O(x^5) sin(x)=x3!x3+O(x5)
  它的 3 / 3 3/3 3/3阶帕德逼近形式为:
R 2 , 2 ( x ) = a ( x ) b ( x ) = a 0 + a 1 x + a 2 x 2 1 + b 1 x + b 2 x 2 R_{2,2}(x)=\frac{a(x)}{b(x)}\\ =\frac{a_0+a_1x+a_2x^2}{1+b_1x+b_2x^2} R2,2(x)=b(x)a(x)=1+b1x+b2x2a0+a1x+a2x2
  要求泰勒级数去掉尾项后减去帕德逼近要约等于0,于是误差只在泰勒级数尾项中:
x − x 3 3 ! − a 0 + a 1 x + a 2 x 2 1 + b 1 x + b 2 x 2 ≈ 0 x-\frac{x^3}{3!}-\frac{a_0+a_1x+a_2x^2}{1+b_1x+b_2x^2}\approx0 x3!x31+b1x+b2x2a0+a1x+a2x20
  移动一下,将约等号变成等号:
x − x 3 3 ! = a 0 + a 1 x + a 2 x 2 1 + b 1 x + b 2 x 2 ( x − x 3 3 ! ) ( 1 + b 1 x + b 2 x 2 ) = a 0 + a 1 x + a 2 x 2 x-\frac{x^3}{3!}=\frac{a_0+a_1x+a_2x^2}{1+b_1x+b_2x^2}\\ (x-\frac{x^3}{3!})({1+b_1x+b_2x^2})={a_0+a_1x+a_2x^2} x3!x3=1+b1x+b2x2a0+a1x+a2x2(x3!x3)(1+b1x+b2x2)=a0+a1x+a2x2
  将左边展开:
( x − x 3 3 ! ) ( 1 + b 1 x + b 2 x 2 ) = x − 1 6 x 3 + b 1 x 2 − 1 6 b 1 x 4 + b 2 x 3 − 1 6 b 2 x 5 + = x + b 1 x 2 + ( b 2 − 1 6 ) x 3 − 1 6 b 1 x 4 − 1 6 b 2 x 5 (x-\frac{x^3}{3!})({1+b_1x+b_2x^2})\\ =x-\frac16x^3+\\ b_1x^2-\frac16b_1x^4+\\ b_2x^3-\frac16b_2x^5+\\ =x+b_1x^2+(b_2-\frac16)x^3-\frac16b_1x^4-\frac16b_2x^5 (x3!x3)(1+b1x+b2x2)=x61x3+b1x261b1x4+b2x361b2x5+=x+b1x2+(b261)x361b1x461b2x5
  所以有:
x + b 1 x 2 + ( b 2 − 1 6 ) x 3 − 1 6 b 1 x 4 − 1 6 b 2 x 5 = a 0 + a 1 x + a 2 x 2 x+b_1x^2+(b_2-\frac16)x^3-\frac16b_1x^4-\frac16b_2x^5\\ = a_0+a_1x+a_2x^2 x+b1x2+(b261)x361b1x461b2x5=a0+a1x+a2x2
  左边减去右边,有:
− a 0 + ( 1 − a 1 ) x + ( b 1 − a 2 ) x 2 + ( b 2 − 1 6 ) x 3 − 1 6 b 1 x 4 − 1 6 b 2 x 5 = 0 -a_0+(1-a_1)x+(b_1-a_2)x^2+(b_2-\frac16)x^3-\frac16b_1x^4-\frac16b_2x^5=0 a0+(1a1)x+(b1a2)x2+(b261)x361b1x461b2x5=0
  把最高次的 x 5 x^5 x5项忽略,剩余的每项等于0,可以得到一个方程组:
− a 0 = 0 1 − a 1 = 0 b 1 − a 2 = 0 b 2 − 1 6 = 0 − 1 6 b 1 = 0 -a_0=0\\ 1-a_1=0\\ b_1-a_2=0\\ b_2-\frac16=0\\ -\frac16b_1=0\\ a0=01a1=0b1a2=0b261=061b1=0
  把上面的方程组解开得:
b 1 = 0 b 2 = 1 6 b_1=0\\ b_2=\frac1{6} b1=0b2=61
  代入得:
a 0 = 0 a 1 = 1 a 2 = 0 a_0=0\\ a_1=1\\ a_2=0 a0=0a1=1a2=0
  于是最终 s i n ( x ) sin(x) sin(x) 2 / 2 2/2 2/2阶帕德逼近为:
R 2 , 2 ( x ) = x 1 + 1 6 x 2 R_{2,2}(x)=\frac{x}{1+\frac{1}{6}x^2} R2,2(x)=1+61x2x
  在坐标系上,帕德逼近和原函数在越接近于0的地方,误差越小,离0越远,就误差越大,越来越离,如 R 2 , 2 R_{2,2} R2,2 s i n ( x ) sin(x) sin(x)的图像如下:
在这里插入图片描述






















通解

python代码

  python代码就是根据泰勒级数求帕德逼近的 m + n + 1 m+n+1 m+n+1个未知参数。当然,根据方程求麦克劳林级数(在 x = 0 x=0 x=0点的泰勒级数)的代码我是不会写的,因为场景太多太复杂。对于解方程,我这里复制了一个简单的矩阵类:

# -*- coding:UTF-8 -*- import copy import sympy from sympy.core.expr import Expr MAX_ERROR = 1e-15 def solve(l, u, values): n = len(values) y = [0] * n for i in range(0, n): # # 遍历其后的所有行,让values的值减掉 y[i] = values[i] for j in range(i + 1, n): values[j] -= l.lines[j][i] * values[i] # 再解Ux=y solution = [0] * n for i in range(n - 1, -1, -1): solution[i] = round(y[i] / u.lines[i][i], 2) for j in range(0, i): y[j] -= u.lines[j][i] * solution[i] return solution class Matrix: # 矩阵 def __init__(self, lines): self.__lines = lines def __str__(self): result: str = '' for line in self.__lines: result += str(line) result += '\n' return result # 暴露属性 @property def lines(self): return self.__lines def append(self, matrix): # define an array rows = len(self.__lines) columns = len(self.__lines[0]) + len(matrix.__lines[0]) unit = [[0 for _ in range(0, columns)] for _ in range(0, rows)] for y in range(0, len(unit)): self_line = self.__lines[y] other_line = matrix.__lines[y] # 0~ this this ~other for i in range(0, len(self_line)): unit[y][i] = self_line[i] for i in range(0, len(other_line)): unit[y][i + len(self_line)] = other_line[i] return Matrix(unit) def sub_matrix(self, row_start, row_end, column_start, column_end): unit = [[self.__lines[i][j] for j in range(column_start, column_end)] for i in range(row_start, row_end)] return Matrix(unit) def __sub__(self, other): return Matrix([[e - other.__lines[y][x] for x, e in enumerate(line)] for y, line in enumerate(self.__lines)]) # LUP分解 def lup_decomposition(self): n = len(self.__lines) # 初始化L U为同样大小的0矩阵 l = copy.deepcopy(self.__lines) u = copy.deepcopy(self.__lines) a = copy.deepcopy(self.__lines) p = [i for i in range(0, n)] # i代表处理的行和列,因为每次都是从左上到右下不断缩小这个矩阵 for i in range(0, n): # 先求出自己的置换 # 注意不能用是否等于0判断,而是考虑浮点数精度进行判断 # 找最大绝对值 swap_line = i # 进行置换 # 找出不为0的一行,进行置换 for k in range(i + 1, n): if abs(a[k][i]) > abs(a[swap_line][i]): swap_line = k if abs(a[swap_line][i]) <= MAX_ERROR: raise Exception("矩阵无解") # 交换两行 a[i], a[swap_line] = a[swap_line], a[i] l[i], l[swap_line] = l[swap_line], l[i] p[i], p[swap_line] = p[swap_line], p[i] # 剩下的就是LU分解了 # 每一行的l进行赋值 # 这个循环其实是对角线方向 l[i][i] = 1 u[i][i] = a[i][i] for j in range(i + 1, n): l[i][j] = 0 l[j][i] = a[j][i] / a[i][i] u[i][j] = a[i][j] u[j][i] = 0 # 处理A比较麻烦,需要计算舒尔补 for k in range(i + 1, n): # 舒尔补是 a[j][k] -= a[j][i] * a[i][k] / a[i][i] # 因为前面计算l[j][i] = a[j][i] / a[i][i] # 所以可以直接替换 a[j][k] = round(a[j][k] - l[j][i] * a[i][k], 2) return Matrix(l), Matrix(u), Matrix([[1 if j == i else 0 for j in range(0, n)] for i in p]) def solve_by_lup(self, values): l, u, p = self.lup_decomposition() # lux=pb pb = p * Matrix([[num] for num in values]) pb_values = [i[0] for i in pb.__lines] # 因为l是单位下三角矩阵,所以直接代入法 return solve(l, u, pb_values) 

  接下来是我的帕德代码,里面包含了有理函数类:

from com.youngthing.mathalgorithm.matrix import Matrix class Rational: def __init__(self, a, b): self.__numerator = list(reversed(a)) self.__denominator = list(reversed(b)) def __call__(self, *args, kwargs): return Rational.calc(self.__numerator, args[0]) / Rational.calc(self.__denominator, args[0]) def __str__(self): a_str = Rational.poly_str(self.__numerator) b_str = Rational.poly_str(self.__denominator) return f"({ 
     a_str})/({ 
     b_str})" @staticmethod def calc(polynomial, val): result = 0 for coefficient in polynomial: result = result * val + coefficient return result @staticmethod def poly_str(polynomial): result = '' first = True n = len(polynomial) for i, coefficient in enumerate(polynomial): if coefficient == 0: continue if first: first = False else: result += '+' if coefficient != 1: result += str(coefficient) + '*' order = n - i - 1 if order > 0: result += 'x' else: result += '1' if order > 1: result += '' + str(order) return result def pade(coefficients, m, n): # 第一步解线性方程组,得出分母所有系数 lines = [] for i in range(0, n): line = list(reversed(coefficients[i + 1:i + n + 1])) lines.append(line) matrix = Matrix(lines) values = [-x for x in coefficients[m + 1:m + n + 1]] b = matrix.solve_by_lup(values) b.insert(0, 1) a = [] for i in range(0, m + 1): a_i = 0 for j in range(0, i + 1): a_i += coefficients[j] * b[i - j] a.append(a_i) return Rational(a, b) 

测试

  我们以sin(x)的 3 , 3 3,3 3,3阶为例子,进行测试。首先计算 sin ⁡ ( x ) \sin(x) sin(x)的麦克劳林级数:
s i n ( x ) = x − x 3 3 ! + x 5 5 ! + O ( x 7 ) sin(x)=x-\frac{x^3}{3!}+\frac{x^5}{5!}+O(x^7) sin(x)=x3!x3+5!x5+O(x7)
  那么测试数据输入单元测试,写出测试代码:

import unittest from com.youngthing.mathalgorithm.approximate.pade import pade from com.youngthing.mathalgorithm.permutations.heap import permutations class MyTestCase(unittest.TestCase): def test_sin_3_3(self): coefficients = [0, 1, 0, -1 / 6, 0, 1 / 120, 0] r = pade(coefficients, 3, 3) print(r) if __name__ == '__main__': unittest.main() 

  测试结果:

(-0.*x3+x)/(0.06*x2+1) 

  用LaTex plot一下,看看结果:

\documentclass[a4paper,UTF8]{ 
   article} \usepackage{ 
   ctex} \usepackage{ 
   tikz} \begin{ 
   document} \begin{ 
   tikzpicture} % 画坐标系 \draw (0,-3) [->, thick]-- (0,3) node (yaxis) [above] {y}; \draw (-4,0) [->, thick]-- (4,0) node (yaxis) [above] {x}; % 画X轴 \foreach \x in { 
   -4,-3,-2,-1,1,2,3,4}{ 
    \node at (\x,0) [below] { 
   \x}; \draw (\x,0) -- (\x, 2pt); } \foreach \y in { 
   -3,-2,-1,0,1,2,3}{ 
    \node at (0,\y) [left] { 
   \y}; } \foreach \y in { 
   -3,-2,-1,1,2,3}{ 
    \draw (0,\y) -- (2pt,\y); } \draw [blue,smooth,domain=-4:4] plot function { 
   sin(x)}; \draw [green,smooth,domain=-4:4] plot function { 
   (-0.*x**3+x)/(0.06*x**2+1)}; \end{ 
   tikzpicture} \end{ 
   document} 

  结果如下,可以看到,比 2 / 2 2/2 2/2阶的帕德逼近精确多了:
在这里插入图片描述

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/125776.html

(0)
上一篇 2025-09-24 14:10
下一篇 2025-09-24 14:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信