https://pastein.ru/t/RFX
скопируйте уникальную ссылку для отправки
from math import sin, cos
import numpy as np
from scipy.interpolate import CubicSpline
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
# n - число точек! Не степень!
BUILD_N = 15 + 1 # число точек для построения сплайна
COMPARE_N = 100 + 1 # число точек для сравнения по норме (значения записываются в файл)
A, B = -3, 3 # границы
def f1(x):
return sin(cos(x))
def d_f1(x):
return -sin(x) * cos(cos(x))
def d2_f1(x):
return -sin(cos(x))*(sin(x)**2) - cos(cos(x))*cos(x)
def linspace(a, b, n):
"""
Возвращает n равностоящих узлов на отрезке [a, b]
"""
return np.linspace(a, b, n)
def count_func(f, a, b, n, numbers_by_interval_func=linspace):
"""
Считает значение функции на отрезке [a, b] в каждой из n точек
Использует функцию-генератор узлов (numbers_by_interval_func)
по умолчанию np.linspace - равностоящие узлы
Возвращает массив пар вида [(x1, y1), (x2, y2), ...]
"""
x_values = list(numbers_by_interval_func(a, b, n))
y_values = list(map(f, x_values))
return list(zip(x_values, y_values))
def plot_func(f, a, b, n, cur_plt=plt, plot_kwargs=None):
"""
Строить функцию f на отрезке a, b по n равностоящим точкам в matplotlib
"""
if plot_kwargs is None:
plot_kwargs = {}
x_values = np.linspace(a, b, n)
y_values = list(map(f, x_values))
cur_plt.plot(x_values, y_values, **plot_kwargs)
def build_spline(f, a, b, n=BUILD_N):
"""
Строит функцию - кубический сплайн по отрезку и числу точек
Возвращает функцию для приблизительного рассчета значения в точке
"""
# считаем значения в n точках
func_prec_points = count_func(f, a, b, n, numbers_by_interval_func=linspace)
# в левой границе совпадает первая производная
d_f1_a = d_f1(a)
# в правой границе совпадает вторая производная
d2_f1_b = d2_f1(b)
# unzipping points
x, y = list(zip(*func_prec_points))
x, y = list(x), list(y)
# считаем сплайн
return CubicSpline(x, y, bc_type=((1, d_f1_a), (2, d2_f1_b)))
def build_matr(points, a, b, n):
# unzipping points
x, y = list(zip(*points))
x, y = list(x), list(y)
# в левой границе совпадает первая производная
d_f1_a = d_f1(a)
# в правой границе совпадает вторая производная
d2_f1_b = d2_f1(b)
h = (b-a) / n
# совпадение по первой производной
ldiag = []
cdiag = [h/3]
rdiag = [h/6]
vals = []
vals.append((y[1] - y[0])/h - d_f1_a)
for i in range(1, n):
ldiag.append(h/6)
cdiag.append(2*h/3)
rdiag.append(h/6)
vals.append(
(y[i+1] - 2 * y[i] + y[i-1]) / h
)
# совпадение по второй производной
ldiag.append(0)
cdiag.append(1)
vals.append(d2_f1_b)
return [ldiag, cdiag, rdiag, vals]
def get_moments(matr, n):
x_i, r_i = 0, 0
coeff = []
# прямой ход
for i in range(n+1):
a = 0 if i == 0 else matr[0][i - 1]
b = -1 * matr[1][i]
c = 0 if i == n else matr[2][i]
delta = b - a * x_i
x_i = c / delta
r_i = (a * r_i - matr[3][i]) / delta
coeff.append([x_i, r_i])
# обратный ход
x = 0
res = [0]*(n+1)
for i in range(n, -1, -1):
x = coeff[i][0] * x + coeff[i][1]
res[i] = x
return res
def my_build_spline(f, a, b, n=BUILD_N-1):
"""
Строит функцию - кубический сплайн по отрезку и степени(!!!)
Возвращает функцию для приблизительного рассчета значения в точке
"""
func_prec_points = count_func(f, a, b, n+1, numbers_by_interval_func=linspace)
matr = build_matr(func_prec_points, a, b, n)
moments = get_moments(matr, n)
# unzipping points
x_prec, y_prec = list(zip(*func_prec_points))
x_prec, y_prec = list(x_prec), list(y_prec)
def func(x):
pos = 0 # промужеток, которому принадлежит x
h = (b-a) / n
for i in range(1, n+1):
if x_prec[i - 1] <= x <= x_prec[i]:
pos = i
break
res = 0
res += moments[pos-1] * pow((x_prec[pos]-x), 3) / (6*h)
res += moments[pos] * pow(x-x_prec[pos-1], 3) / (6*h)
res += (y_prec[pos-1] - (h*h/6)*moments[pos-1]) * (x_prec[pos]-x)/h
res += (y_prec[pos] - (h*h/6)*moments[pos]) * (x-x_prec[pos-1])/h
return res
return func
def compare_funcs_and_count_eps(base_func, approx_func, a, b, n):
"""
Считает норму двух функций по числу равностоящих точек
"""
base_points = count_func(base_func, a, b, n)
base_values = [e[1] for e in base_points] # только y
approx_points = count_func(approx_func, a, b, n)
approx_values = [e[1] for e in approx_points] # только y
eps = max([abs(base_value - approx_value)
for base_value, approx_value
in zip(base_values, approx_values)])
return eps
def compare_and_plot(base_func, approx_func, a, b, plot_n, compare_n, cur_plt=plt):
"""
Считает норму для двух функций на отрезке с заданным числом равностоящих узлов
После чего строит обе функции на равностоящих узлах
:param base_func: точная функция
:param approx_func: приближенная функция
:param a: левая граница
:param b: правая граница
:param plot_n: число узлов для построения графика
:param compare_n: число узлов для расчёта нормы
:param cur_plt: matplotlib.pyplot на котором строить графики
:return: максимум-норму на compare_n равностоящих узлах
"""
eps = compare_funcs_and_count_eps(base_func, approx_func, a, b, compare_n)
plot_func(base_func, a, b, plot_n, cur_plt, plot_kwargs={
'label': 'base'
})
plot_func(approx_func, a, b, plot_n, cur_plt, plot_kwargs={
'label': 'approximate'
})
# plot_func(lambda e: abs(approx_func(e) - base_func(e)), a, b, plot_n, cur_plt, plot_kwargs={
# 'label': 'eps'
# })
cur_plt.legend(prop={'size': 6})
return eps
def main():
# строим сплайн
# func_spline = build_spline(f1, A, B, BUILD_N)
func_spline = my_build_spline(f1, A, B, BUILD_N-1)
# сравниваем в равностоящих узлах
eps_p = compare_and_plot(f1, func_spline, A, B, plot_n=COMPARE_N, compare_n=COMPARE_N,
cur_plt=plt)
print(f'Погрешность: {eps_p}')
plt.show()
if __name__ == '__main__':
main()