微分方程是描述客观世界强有力的工具,它可以反映事物某些内在属性与外在表象之间的关系。但其实微分方程既不好写,也不好解,比如麦克斯韦站在法拉第、汤姆逊等实验大家的肩膀上,凭借他高深的数学造诣和丰富的想象力也花了十余年才将大笔一挥,写下麦克斯韦方程组,一统电磁。还有至今俯视人类的纳维斯托克斯方程组,170余年的时光里,很多人为之奉献了一生的努力,但遗憾的是,我们仍未能找到解析解。 虽然微分方程解析解不易求得,但在工程领域广泛应用的数值解、近似解,一直在迅猛发展。
求微分方程的近似解的思路是,先取原函数的近似解,将近似代入微分方程,即可将复杂的微分方程转化为一般的线性方程组,而求解线性方程组就是简单的事情了。
取近似解的思路是,先确定一组线性无关的基函数,再线性叠加基函数,就可获得近似解。 工程领域常用的傅里叶展开,就是选取一组线性无关的三角函数作为基函数,线性叠加基函数后,就获得了原函数的近似解。
另一种常用的方式是将多项式作为基函数。
由泰勒展开可知,所取多项式的项数越高,则近似解会越逼近原函数。
但有得必有舍,因为所取多项式的系数均为未知数,若取的项数越多,则产生的未知数也越多,而解出多项式系数的线性方程组依赖的是边界条件,n个条件解n个未知数,所以当边界条件数较少时,所取多项式的项数也会有限制,本文将展示一种基于Sympy库的,针对一维问题,给定任意边界条件,生成基函数的算法。
算法主要思想:
算法主要步骤:
2.1以类的形式封装边界条件的信息,便于后续使用该条件的各项信息
单独写入boundary_condition文件,以下为该文件完整代码
class BoundaryCondition:
def __init__(self,
list_x: list, # 自变量的list
value, # 该边界条件的值
list_rank: list, # 各项的微分次数list
list_factor: list, # 各项的系数
list_relation: list # 各项与总表达式的关系(加减乘除)
):
self.list_x = list_x
self.value = value
self.list_rank = list_rank
self.list_factor = list_factor
self.list_relation = list_relation
将以下所有方法写入base_expression文件,统一管理
2.2根据边界条件个数确定近似解多项式
此处用到了一个自动循环定义符号变量的技巧
import sympy as sp
# 设置全局精度变量,控制每次赋值运算的有效位数
precision_value = 5
def get_basic_approximate_expression(condition_num):
""" 生成底层的近似u函数,传入已知边界条件个数,n个边界条件生成的插值函数最高n次 """
expression_u = 0
for i in list(range(condition_num + 1)): # 基于边界条件个数n,循环生成n个多项式基函数
index = "a" + str(i)
item = sp.symbols('%s' % index)
x = sp.symbols('x')
expression_u += item * x ** i
return expression_u
2.3将边界条件的微分信息计算到近似解多项式的表达式中
若为0阶,不微分,高阶则进行循环微分
def get_expression_from_boundary(expression, rank):
""" 从边界条件充实底层近似函数 """
temp_expression = expression
if rank != 0: # 微分次数不为0时,进行循环微分
for _ in list(range(rank)):
temp_expression = sp.diff(temp_expression, 'x')
return temp_expression
2.4将边界条件的自变量取值的信息计算到近似解多项式的表达式中
def get_equation_from_boundary(expression, value_x, precision=precision_value):
""" 从边界条件获取仅含系数a的方程 """
return expression.subs('x', value_x, n=precision)
2.5求解a系数的关系
传入方程组以及对应的值,通过判断方程组未知数的个数来确定待消元的n个未知数,最后求解方程,返回a系数之间的关系(所有未知数可以由一个未知数来表示了)
def get_factor_relation(list_equation, list_value):
""" 解方程,获取a系数之间的关系 """
if len(list_equation) == len(list_value): # 长度一致说明传入的格式正确
num_list = [] # 用来存储未知数个数
for equation in list_equation:
string_equation = str(equation) # 将方程组转化为字符形式
num_list.append(string_equation.count('a')) # 获取字符形式方程中未知数的个数,依次判断每个方程的未知数个数
item_num = max(num_list) # 只需知道未知数个数的最大值,因为只用来作循环的判断条件
list_unknown_num = [] # 用来存储待消元的未知数
complete_equation_list = []
n = len(list_equation) + 1 # n个方程说明实际有n+1个未知数
from_num = n - item_num # 用来判断,是否有未知数在之前的代值过程中被消掉了
# 如果最大值已经小于未知数个数,说明方程是可解的!
if n - item_num == 0:
for i in list(range(n)):
index = "a" + str(i)
list_unknown_num.append(index)
elif n - item_num > 0:
for i in list(range(from_num, n)): # 此处还存在瑕疵,存在一个很强的假定条件:最后的an没有被消掉
index = "a" + str(i)
list_unknown_num.append(index)
for j in list(range(len(list_equation))):
temp = list_equation[j] - list_value[j]
complete_equation_list.append(temp)
return sp.solve(complete_equation_list, list_unknown_num)
2.6传入系数间的关系、基函数,插值出多项式表达式
def get_upper_approximate_expression(factor_relation, basis_expression):
""" 插值出近似解的多项式表达式 """
high_expression = basis_expression
string_equation = str(high_expression)
item_num = string_equation.count('a') # 获取底层近似函数中未知数的个数
list_index = [] # 存储全部ai数列符号
list_result_key = []
for i in list(range(item_num)):
list_index.append("a" + str(i))
for key in factor_relation:
list_result_key.append(str(key))
for index in list_index:
# 将表达式替换为用一个a表示
if index in list_result_key:
sym = sp.symbols(index)
high_expression = high_expression.subs({sym: factor_relation[sym]})
# 需要将剩余的未知数置为1时,请取消注释
# elif len(list_index) - len(list_result_key) == 1: # 将其赋为1
# high_expression = high_expression.evalf(subs={index: 1})
# else:
# high_expression = high_expression.evalf(subs={index: 0})
return high_expression
2.7集成上面的方法,统一处理,最终返回一个多项式表达式
def get_approximate_expression(list_boundary_condition):
""" 主函数,集成运算后返回最终的多项式表达式 """
list_equation = []
list_value = []
basic_approximate_expression = get_basic_approximate_expression(len(list_boundary_condition))
for boundary in list_boundary_condition:
# 把边界条件的值放到统一的list中
list_value.append(boundary.value)
# 获取此边界条件的项数
item_num = len(boundary.list_x)
equation = 0
for i in list(range(item_num)):
expression = get_expression_from_boundary(basic_approximate_expression,
rank=boundary.list_rank[i])
temp_equation = get_equation_from_boundary(expression=expression, value_x=boundary.list_x[i])
# 拼接到此边界条件的大方程中去
if boundary.list_relation[i] == "add": # 加法标记符,执行加法
equation = equation + boundary.list_factor[i] * temp_equation
elif boundary.list_relation[i] == "sub": # 减法标记符,执行减法
equation = equation - boundary.list_factor[i] * temp_equation
elif boundary.list_relation[i] == "tim": # 乘法法标记符,执行乘法
equation = equation * boundary.list_factor[i] * temp_equation
elif boundary.list_relation[i] == "div": # 除法标记符,执行除法
equation = equation / boundary.list_factor[i] * temp_equation
list_equation.append(equation) # 执行一次循环后,将处理好的方程保存到方程list中
factor_relation = get_factor_relation(list_equation, list_value)
return get_upper_approximate_expression(factor_relation, basis_expression=basic_approximate_expression)
1.边界条件:
2.main文件设置:
from boundary_condition import BoundaryCondition
import base_expression as be
# 设置边界条件1
boundary_1 = BoundaryCondition(list_x=[0], # 设置边界条件的自变量
value=0, # 设置边界条件的值
list_rank=[0], # 设置边界条件中各项的阶数
list_factor=[1], # 设置边界条件中各项的系数
list_relation=["add"]) # 设置边界条件中各项的关系
# 设置边界条件2
boundary_2 = BoundaryCondition(list_x=[1],
value=0,
list_rank=[1],
list_factor=[1],
list_relation=["add"])
result = be.get_approximate_expression([boundary_1, boundary_2])
print("插值出的多项式表达式", result)
3.结果:
1.边界条件:
2.main文件设置:
from boundary_condition import BoundaryCondition
import base_expression as be
# 设置边界条件1
boundary_1 = BoundaryCondition(list_x=[0], # 设置边界条件的自变量
value=0, # 设置边界条件的值
list_rank=[0], # 设置边界条件中各项的阶数
list_factor=[1], # 设置边界条件中各项的系数
list_relation=["add"]) # 设置边界条件中各项的关系
# 设置边界条件2
boundary_2 = BoundaryCondition(list_x=[1],
value=1,
list_rank=[0],
list_factor=[1],
list_relation=["add"])
result = be.get_approximate_expression([boundary_1, boundary_2])
print("插值出的多项式表达式", result)
3.结果:
1.边界条件:
2.main文件设置:
from boundary_condition import BoundaryCondition
import base_expression as be
# 设置边界条件1
boundary_1 = BoundaryCondition(list_x=[0, 1], # 设置边界条件的自变量
value=7, # 设置边界条件的值
list_rank=[0, 0], # 设置边界条件中各项的阶数
list_factor=[1, 2], # 设置边界条件中各项的系数
list_relation=["add", "add"]) # 设置边界条件中各项的关系
# 设置边界条件2
boundary_2 = BoundaryCondition(list_x=[0, 2],
value=-1,
list_rank=[1, 2],
list_factor=[1, -1],
list_relation=["add", "add"])
result = be.get_approximate_expression([boundary_1, boundary_2])
print("插值出的多项式表达式", result)
3.结果:
此算法尚有不足,存在两个问题
后续会继续优化
源码github地址:https://github.com/base_function