Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Аппроксимация методом наименьших квадратов на базе полиномов Эрмита на Haskell

.pdf
Скачиваний:
0
Добавлен:
02.01.2026
Размер:
1.24 Mб
Скачать

Министерство цифрового развития, связи и массовых коммуникаций Российской Федерации

Федеральное государственное бюджетное образовательное учреждение Высшего образования «Санкт-Петербургский государственный университет телекоммуникаций им. Проф. М. А. Бонч-Бруевича» (СПбГУТ)

Факультет Информационных технологий и программной инженерии

Кафедра Программной инженерии

Лабораторная работа 6

По дисциплине: Разработка приложений искусственного интеллекта в киберфизических системах

Выполнил студент: Яковлев М. А. ИКПИ-32

Принял работу: Ерофеев С. А.

Дата выполнения: «21» декабря 2025 г.

Санкт-Петербург

2025 г.

Постановка задачи

Разработать программу аппроксимации методом наименьших квадратов на базе полиномов Эрмита. Многочлен строится как сумма a0H0(x)+a1H1(x) ... amHm(x), где H1(x) ... Hm(x) — полином Эрмита.

Значение m вводит пользователь. В качестве исходных данных считываются пары координат x1y1 ... xnyn в файле. Исключаются дубликаты, контролируется неоднозначность.

Пары сортируются по возрастанию Х методом нисходящего слияния. Программа вычисляет коэффициенты а0, а1...am. Пользователь вводит количество точек n для построения графика.

Для достижения поставленной цели необходимо выполнить следующие задачи:

реализовать считывание пар координат (xi, yi) из входного файла;

исключить дубликаты данных и проконтролировать неоднозначность значений;

выполнить сортировку точек по возрастанию аргумента методом сортировки нисходящего слияния;

обеспечить ввод пользователем степени аппроксимирующего полинома;

реализовать вычисление коэффициентов полинома методом наименьших квадратов;

выполнить оценку качества аппроксимации;

сгенерировать точки для построения графика аппроксимирующей функции;

Входные данные:

Входные данные задаются во внешнем текстовом файле и представляют собой набор пар координат следующего вида:

x1 y1 x2 y2

...

xn yn

Каждая строка содержит два вещественных числа — значение аргумента и соответствующее значение функции.

Выходные данные:

Программа формирует следующие результаты:

список отсортированных исходных точек;

коэффициенты аппроксимирующего полинома;

значения аппроксимирующей функции в исходных точках;

набор вычисленных точек (, ) для последующего построения графика.

Алгоритм программы

Суть алгоритма заключается в следующем: разработанная программа реализует процедуру аппроксимации экспериментальных данных полиномиальной функцией методом наименьших квадратов. Логика работы программы состоит из последовательных этапов.

1. Структура данных и функциональные компоненты

Впрограмме используются следующие основные типы данных:

Point — пара вещественных чисел (Double, Double), представляющая точку ( , );

векторы и матрицы: Vector и Matrix.

Функциональные компоненты программы включают:

2

ввод и предварительную обработку данных;

сортировку точек;

построение матрицы;

вычисление коэффициентов полинома методом наименьших квадратов;

оценку погрешности аппроксимации;

генерацию точек для визуализации.

2.Загрузка и предварительная обработка данных

Программа считывает данные из файла, имя которого вводится пользователем. Алгоритм обработки входного файла включает:

построчное чтение данных;

преобразование строк в пары чисел;

игнорирование некорректных строк. Далее выполняется удаление дубликатов:

если точки имеют одинаковое значение и одинаковое , они считаются полными дубликатами и игнорируются;

если значения совпадают, но различаются — фиксируется неоднозначность.

3.Сортировка данных

После удаления дубликатов точки сортируются по возрастанию координаты . Для сортировки используется метод сортировки нисходящим слиянием. На этапе разделения массив точек виртуально делится на две примерно равные половины, и процесс рекурсивно применяется к каждой из них. Это продолжается до тех пор, пока не будут получены минимальные части, состоящие из одного элемента.

На этапе слияния происходит объединение двух уже отсортированных подмассивов в один упорядоченный массив. Процедура слияния сравнивает первые элементы обоих массивов, выбирает меньший по значению x и помещает его в результирующий массив, затем перемещает указатель в массиве, из которого был взят элемент. Этот процесс повторяется до полного исчерпания обоих массивов. Если один из массивов заканчивается раньше, оставшиеся элементы второго массива просто копируются в результат без дополнительных сравнений.

4. Выбор степени аппроксимирующего полинома

Пользователь вводит степень полинома . Программа выполняет проверку корректности ввода:

степень должна быть неотрицательной;

при слишком высокой степени выводится предупреждение о возможной некорректной аппроксимации.

Максимально допустимая степень полинома определяется как десять.

5. Аппроксимация методом наименьших квадратов

Для вычисления коэффициентов аппроксимирующего полинома используется метод наименьших квадратов. Формируется матрица А, где каждый элемент определяется как Hj(xi). Вектор правых частей содержит значения = .

Система уравнений решается с использованием метода Гаусса. Результатом является вектор коэффициентов полинома:

a = (0, 1, … , )

6. Генерация точек для построения графика

Пользователь задаёт количество точек, которые необходимо вычислить на интервале от минимального до максимального значения .

3

Для равномерно распределённых значений аргумента вычисляются соответствующие значения аппроксимирующего полинома.

Алгоритм Гаусса

Метод состоит из двух основных этапов: прямого хода и обратного хода. Прямой ход преобразует матрицу системы к верхнетреугольному виду путём последовательного исключения переменных.

1.На каждом шаге выбирается ведущий элемент — максимальный по модулю элемент в текущем столбце. Выбор максимального элемента повышает устойчивость алгоритма и уменьшает погрешность вычислений.

2.Если ведущий элемент оказывается слишком мал (менее 10 ¹²), программа сообщает о вырожденности матрицы.

3.После приведения матрицы к треугольному виду выполняется обратный ход — последовательное вычисление значений неизвестных, начиная с последнего уравнения.

4.Каждое найденное значение подставляется в предыдущие уравнения, что позволяет определить все коэффициенты полинома.

Алгоритм сортировки слиянием

Метод состоит из двух основных этапов: разделения и слияния.

1.На этапе разделения исходный массив рекурсивно делится на две примерно равные части до тех пор, пока не будут получены подмассивы длиной 0 или 1 элемент, которые считаются отсортированными по определению.

2.После достижения минимальных фрагментов начинается этап слияния — отсортированные подмассивы объединяются в один упорядоченный массив с сохранением порядка элементов.

3.Процедура слияния сравнивает первые элементы двух упорядоченных массивов, выбирает меньший по значению x и помещает его в результирующий массив.

4.Этот процесс повторяется до полного исчерпания обоих массивов.

5.Если один из массивов заканчивается раньше, оставшиеся элементы второго массива копируются в результат без дополнительных сравнений.

Перечень функций

Ввод/вывод данных:

1.requestFilePath — запрос пути к файлу с проверкой существования

2.readPoints — чтение точек (x y) из файла с удалением дубликатов

3.writeResultsToFile — сохранение результатов в файл с коэффициентами

4.printResultsTable — табличный вывод исходных и аппроксимированных значений

Обработка дубликатов:

1.handleDuplicateXs — интерактивная обработка точек с одинаковыми X (усреднение/мин/макс)

4

2.mergeSort — стабильная сортировка слиянием для группировки дубликатов

3.nubBy — удаление полных дубликатов (x,y с точностью 1e-9)

Математика полиномов Эрмита:

1.hermite — рекуррентное вычисление полинома Эрмита степени n

2.hermiteVector — вектор базисных полиномов [H0(x), H1(x), ..., Hm(x)]

3.evalHermitePoly — вычисление значения полинома по коэффициентам

Метод наименьших квадратов:

1.buildSystem — построение матрицы Грама и правой части с весами

2.gaussElimination — метод Гаусса с выбором главного элемента

3.solveLeastSquares — полное решение задачи МНК в базисе Эрмита

Визуализация:

1.linspace — равномерная сетка точек для графика

2.asciiPlot — ASCII-график (70x20) с осями и легендой (* точки, + кривая)

3.calculateRMSE — среднеквадратичная ошибка аппроксимации

5

Схема

Рисунок 1. Общая схема работы программы

6

Рисунок 2. Метод Гаусса

Рисунок 3. Удаление дубликатов

7

Рисунок 4. Считывание точек из файла

Рисунок 5. Поиск дубликатов

8

Программа

{-# LANGUAGE ScopedTypeVariables #-} import Data.List (nubBy, foldl', groupBy) import Data.Function (on)

import System.IO import Text.Printf

import Text.Read (readMaybe) import Control.Monad (when)

import Control.Exception (catch, SomeException)

type Point = (Double, Double)

type PointWeight = (Double, Double, Double) -- (x, y, weight) type Matrix = [[Double]]

type Vector = [Double]

-- Функция для запроса файла с обработкой ошибок requestFilePath :: IO FilePath

requestFilePath = do

putStrLn "Введите имя файла с данными (формат: x y в каждой строке):" getValidFilePath

getValidFilePath :: IO FilePath getValidFilePath = do

file <- getLine

-- Пытаемся открыть файл для чтения, чтобы проверить его существование result <- catch (do

handle <- openFile file ReadMode hClose handle

return (Right file))

(\(e :: SomeException) -> return (Left (show e)))

case result of Right f -> return f

9

Left err -> do

putStrLn $ "ОШИБКА: Файл не найден или недоступен для чтения"

putStrLn $ "Детали: " ++ takeWhile (/='\n') err -- Берем первую строку ошибки putStrLn "Пожалуйста, введите другое имя файла:"

getValidFilePath

-- Рекуррентная формула для полиномов Эрмита hermite :: Int -> Double -> Double

hermite 0 _ = 1.0 hermite 1 x = 2 * x hermite n x

| n < 0 = 0.0

| otherwise = 2 * x * hermite (n-1) x - 2 * fromIntegral (n-1) * hermite (n-2) x

-- Вычисление нескольких полиномов Эрмита для одной точки hermiteVector :: Int -> Double -> Vector

hermiteVector m x = [hermite k x | k <- [0..m]]

-- Нисходящая сортировка слиянием (merge sort) mergeSort :: (Ord a) => [a] -> [a]

mergeSort [] = [] mergeSort [x] = [x] mergeSort xs =

let (left, right) = splitAt (length xs `div` 2) xs in merge (mergeSort left) (mergeSort right) where

merge :: (Ord a) => [a] -> [a] -> [a] merge [] ys = ys

merge xs [] = xs merge (x:xs) (y:ys)

| x <= y = x : merge xs (y:ys) | otherwise = y : merge (x:xs) ys

10