Добавил:
tg: @petmanchenko Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ПР №2 Анализ алгоритмов и программ.doc
Скачиваний:
1
Добавлен:
02.02.2025
Размер:
280.58 Кб
Скачать

ФЕДЕРАЛЬНОЕ АГЕНТСТВО СВЯЗИ

ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ

«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М.А. БОНЧ-БРУЕВИЧА»

(СПбГУТ)

Кафедра безопасности информационных систем

ОТЧЁТ

по итоговой работе №2 на тему: «Анализ алгоритмов и программ»

по дисциплине «Алгоритмы и структуры данных»

Выполнил: студент группы ИСТ-931, Гетманченко П.А.

«28» сентября 2020 г. ___________/П.А. Гетманченко /

Принял: к.ф.-м.н., доцент, Моисеев И. А.

«28» сентября 2020 г. __________/ И. А. Моисеев /

Основная часть

Цель работы: разработать пользовательский алгоритм для нахождения локального минимума (максимума) функции на заданном интервале одним из трёх способов. Приобрести навыки и умения анализа алгоритма.

Результаты выполнения работы

Задание 3:

Найти локальный минимум (максимум) функции вида f(x) = 𝒙𝟑−𝒙+𝒆−𝒙 на заданном интервале [a,b] с заданной точностью ε > 0 одним из способов:

1) Методом «деления отрезка» пополам;

2) Методом «золотого сечения»;

3) Методом «Фибоначчи».

Провести анализ разработанного алгоритма и программы и сравнить с аналогичным решением с помощью алгоритма «пассивного поиска».

Вход: вид функции, границы отрезка, требуемая точность

Выход (на экране и в файле): таблица пошаговых приближений, количество итераций (шагов поиска), значение точки минимума, значение функции в этой точке.

#include <iostream>

#include <iomanip>

#include <Windows.h>

#include <math.h>

#include <stdio.h>

#include <vector>

#include <ctime>

// значение искомой функции в точке х

float f(float x) {

return x * x * x - x + exp(-x);

}

// Метод «деления отрезка» пополам

void Bisection(float a, float b, float eps)

{

float a0, b0, a1, b1, x_min, x_max;

srand(time(0)); // начальное время

if ((a <= -2 && b <= -2) || (a >= 0 && b >= 0))

{

//std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ПОИСК МИНИМУМА ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

//std::cout << " k | a_k | b_k | alpha_k | beta_k | f(alpha_k) | f(beta_k) | b_k - a_k |\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

a0 = a; // значения переменных a0, b0 будут изменяться в течении цикла

b0 = b; // как бы отрезок будет сужаться каждый раз

bool isEnd = false; // переменная предназначенная для указания завершения цикла while

int steps_min = -1; // согласно таблице приведенной в методичке, нумерация шагов происходит с нуля

while (!isEnd) {

++steps_min; // здесь получаем 0

float delta = (b0 - a0) / 10; // значение этой переменной установлено согласно методичке

float alpha0 = (a0 + b0) / 2 - delta; // аналогично здесь

float beta0 = alpha0 + 2 * delta; // аналогично здесь

float f_alpha = f(alpha0);

float f_beta = f(beta0);

//std::cout << std::fixed;

//std::cout.precision(5);

//std::cout << std::setw(2) << steps_min << " | "

// << std::setw(9) << a0 << " | "

// << std::setw(9) << b0 << " | "

// << std::setw(9) << alpha0 << " | "

// << std::setw(9) << beta0 << " | "

// << std::setw(9) << f_alpha << " | "

// << std::setw(9) << f_beta << " | "

// << std::setw(8) << b0 - a0 << " |\n";

if (f_alpha <= f_beta) {

a1 = a0;

b1 = beta0;

}

else {

a1 = alpha0;

b1 = b0;

}

if (b0 - a0 < eps) { // если условие выполняется, будет конец цикла

isEnd = true; // конец цикла устанавливается здесь

}

else {

a0 = a1;

b0 = b1;

}

}

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

x_min = (a1 + b1) / 2;

std::cout << "\nРезультат вычисления методом «деления отрезка» пополам\n";

std::cout << "Минимальное значение функции на [" << a << ", " << b << "]\n";

std::cout << "y = " << f(x_min) << " точка минимума x = " << x_min << "\n";

std::cout << "Количество шагов = " << steps_min << "\n";

}

else

{

//std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ПОИСК МАКСИМУМА ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

//std::cout << " k | a_k | b_k | alpha_k | beta_k | f(alpha_k) | f(beta_k) | b_k - a_k |\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

a0 = a; // значения переменных a0, b0 будут изменяться в течении цикла

b0 = b; // как бы отрезок будет сужаться каждый раз

bool isEnd = false; // переменная предназначенная для указания завершения цикла while

int steps_max = -1; // согласно таблице приведенной в методичке, нумерация шагов происходит с нуля

while (!isEnd) {

++steps_max; // здесь получаем 0

float delta = (b0 - a0) / 10; // значение этой переменной установлено согласно методичке

float alpha0 = (a0 + b0) / 2 - delta; // аналогично здесь

float beta0 = alpha0 + 2 * delta; // аналогично здесь

float f_alpha = f(alpha0);

float f_beta = f(beta0);

//std::cout << std::fixed;

//std::cout.precision(5);

//std::cout << std::setw(2) << steps_max << " | "

// << std::setw(9) << a0 << " | "

// << std::setw(9) << b0 << " | "

// << std::setw(9) << alpha0 << " | "

// << std::setw(9) << beta0 << " | "

// << std::setw(9) << f_alpha << " | "

// << std::setw(9) << f_beta << " | "

// << std::setw(8) << b0 - a0 << " |\n";

if (f_alpha >= f_beta) {

a1 = a0;

b1 = beta0;

}

else {

a1 = alpha0;

b1 = b0;

}

if (b0 - a0 < eps) { // если условие выполняется, будет конец цикла

isEnd = true; // конец цикла устанавливается здесь

}

else {

a0 = a1;

b0 = b1;

}

}

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

x_max = (a1 + b1) / 2;

std::cout << "\nРезультат вычисления методом «деления отрезка» пополам\n";

std::cout << "Максимальное значение функции на [" << a << ", " << b << "]\n";

std::cout << "y = " << f(x_max) << " точка максимума x = " << x_max << "\n";

std::cout << "Количество шагов = " << steps_max << "\n";

}

std::cout << "Время работы функции = " << clock() / 1000.0 << "\n";

}

// Метод «золотого сечения»

void GoldenSection(float a, float b, float eps)

{

float a0, b0, x_min, x_max;

srand(time(0)); // начальное время

if ((a <= -2 && b <= -2) || (a >= 0 && b >= 0))

{

//std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ПОИСК МИНИМУМА ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

//std::cout << " k | a_k | b_k | alpha_k | beta_k | f(alpha_k) | f(beta_k) | b_k - a_k |\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

a0 = a; // значения переменных a0, b0 будут изменяться в течении цикла

b0 = b; // как бы отрезок будет сужаться каждый раз

int steps_min = -1; // согласно таблице приведенной в методичке, нумерация шагов происходит с нуля

while (std::abs(a0 - b0) >= eps) { // цикл проддолжается до тех пор, пока не получим меньше эпсилон

++steps_min; // здесь получаем 0

float alpha = a0 + 2 * (b0 - a0) / (3 + sqrt(5)); // значение этой переменной устанавливается по методичке, связано со значением золотого сечения

float beta = a0 + 2 * (b0 - a0) / (1 + sqrt(5)); // значение этой переменной устанавливается по методичке, связано со значением золотого сечения

float f_alpha = f(alpha);

float f_beta = f(beta);

//std::cout << std::fixed;

//std::cout.precision(5);

//std::cout << std::setw(2) << steps_min << " | "

// << std::setw(9) << a0 << " | "

// << std::setw(9) << b0 << " | "

// << std::setw(9) << alpha << " | "

// << std::setw(9) << beta << " | "

// << std::setw(9) << f_alpha << " | "

// << std::setw(9) << f_beta << " | "

// << std::setw(8) << b0 - a0 << " |\n";

if (f(alpha) <= f(beta)) {

b0 = beta;

}

else {

a0 = alpha;

}

}

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

x_min = (a0 + b0) / 2;

std::cout << "\nРезультат вычисления методом «золотого сечения»\n";

std::cout << "Минимальное значение функции на [" << a << ", " << b << "]\n";

std::cout << "y = " << f(x_min) << " точка минимума x = " << x_min << "\n";

std::cout << "Количество шагов = " << steps_min << "\n";

}

else

{

//std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ПОИСК МАКСИМУМА ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

//std::cout << " k | a_k | b_k | alpha_k | beta_k | f(alpha_k) | f(beta_k) | b_k - a_k |\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

a0 = a; // значения переменных a0, b0 будут изменяться в течении цикла

b0 = b; // как бы отрезок будет сужаться каждый раз

int steps_max = -1; // согласно таблице приведенной в методичке, нумерация шагов происходит с нуля

while (std::abs(a0 - b0) >= eps) { // цикл проддолжается до тех пор, пока не получим меньше эпсилон

++steps_max; // здесь получаем 0

float alpha = a0 + 2 * (b0 - a0) / (3 + sqrt(5)); // значение этой переменной устанавливается по методичке, связано со значением золотого сечения

float beta = a0 + 2 * (b0 - a0) / (1 + sqrt(5)); // значение этой переменной устанавливается по методичке, связано со значением золотого сечения

float f_alpha = f(alpha);

float f_beta = f(beta);

//std::cout << std::fixed;

//std::cout.precision(5);

//std::cout << std::setw(2) << steps_max << " | "

// << std::setw(9) << a0 << " | "

// << std::setw(9) << b0 << " | "

// << std::setw(9) << alpha << " | "

// << std::setw(9) << beta << " | "

// << std::setw(9) << f_alpha << " | "

// << std::setw(9) << f_beta << " | "

// << std::setw(8) << b0 - a0 << " |\n";

if (f(alpha) >= f(beta)) {

b0 = beta;

}

else {

a0 = alpha;

}

}

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

x_max = (a0 + b0) / 2;

std::cout << "\nРезультат вычисления методом «золотого сечения»\n";

std::cout << "Максимальное значение функции на [" << a << ", " << b << "]\n";

std::cout << "y = " << f(x_max) << " точка максимума x = " << x_max << "\n";

std::cout << "Количество шагов = " << steps_max << "\n";

}

std::cout << "Время работы программы = " << clock() / 1000.0 << "\n";

}

// Метод «Фибоначчи»

void FibonacciSection(float a, float b, float eps)

{

float a0, b0, a1, b1, x_min, x_max;

srand(time(0)); // начальное время

int f0 = 1; // 1-ое значение числа Фибоначчи

int f1 = 1; // 2-ое значение числа Фибоначчи

a0 = a;

b0 = b;

float delta_0 = b0 - a0;

int N = 1;

int k = 0;

std::vector<int> F; // контейнер, в котором будем держать числа Фибоначчи

F.push_back(f0);

F.push_back(f1);

bool isEnd = false; // переменная показывающая завершение цикла

while (!isEnd) {

int f2 = f1 + f0; // вычисляем очередное значение числа Фибоначчи

F.push_back(f2);

++N;

if (delta_0 / f2 <= eps) { // как получим значение <= эпсилон, цикл завершится

isEnd = true;

}

f0 = f1; // обновляем имеющиеся значения чисел Фибоначчи

f1 = f2; // обновляем имеющиеся значения чисел Фибоначчи

}

if ((a <= -2 && b <= -2) || (a >= 0 && b >= 0))

{

//std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ПОИСК МИНИМУМА ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

//std::cout << " k | a_k | b_k | alpha_k | beta_k | f(alpha_k) | f(beta_k) | b_k - a_k |\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

isEnd = false;

int steps_min = -1;

while (!isEnd) {

++steps_min;

// устанавливаются согласно методичке

float delta = b0 - a0;

float alpha = a0 + delta * F[N - k - 2] / F[N - k];

float beta = a0 + delta * F[N - k - 1] / F[N - k];

float f_alpha = f(alpha);

float f_beta = f(beta);

//std::cout << std::fixed;

//std::cout.precision(5);

//std::cout << std::setw(2) << steps_min << " | "

// << std::setw(9) << a0 << " | "

// << std::setw(9) << b0 << " | "

// << std::setw(9) << alpha << " | "

// << std::setw(9) << beta << " | "

// << std::setw(9) << f_alpha << " | "

// << std::setw(9) << f_beta << " | "

// << std::setw(8) << b0 - a0 << " |\n";

if (f_alpha <= f_beta) {

a1 = a0;

b1 = beta;

}

else {

a1 = alpha;

b1 = b0;

}

if (b1 - a1 < eps || N - k == 2) { // если получим разность меньше эпсилон, либо исчерпаем все числа Фибоначчи из нашего контейнера,

// вычисляем корень и выводим его

//float root = (a1 + b1) / 2;

//std::cout << "\nRoot = " << root << "\n";

isEnd = true;

}

else {

a0 = a1;

b0 = b1;

++k;

}

}

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

x_min = (a0 + b0) / 2;

std::cout << "\nРезультат вычисления методом «Фибоначчи»\n";

std::cout << "Минимальное значение функции на [" << a << ", " << b << "]\n";

std::cout << "y = " << f(x_min) << " точка минимума x = " << x_min << "\n";

std::cout << "Количество шагов = " << steps_min << "\n";

}

else

{

//std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ПОИСК МАКСИМУМА ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

//std::cout << " k | a_k | b_k | alpha_k | beta_k | f(alpha_k) | f(beta_k) | b_k - a_k |\n";

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

isEnd = false;

int steps_max = -1;

while (!isEnd) {

++steps_max;

// устанавливаются согласно методичке

float delta = b0 - a0;

float alpha = a0 + delta * F[N - k - 2] / F[N - k];

float beta = a0 + delta * F[N - k - 1] / F[N - k];

float f_alpha = f(alpha);

float f_beta = f(beta);

//std::cout << std::fixed;

//std::cout.precision(5);

//std::cout << std::setw(2) << steps_max << " | "

// << std::setw(9) << a0 << " | "

// << std::setw(9) << b0 << " | "

// << std::setw(9) << alpha << " | "

// << std::setw(9) << beta << " | "

// << std::setw(9) << f_alpha << " | "

// << std::setw(9) << f_beta << " | "

// << std::setw(8) << b0 - a0 << " |\n";

if (f_alpha >= f_beta) {

a1 = a0;

b1 = beta;

}

else {

a1 = alpha;

b1 = b0;

}

if (b1 - a1 < eps || N - k == 2) { // если получим разность меньше эпсилон, либо исчерпаем все числа Фибоначчи из нашего контейнера,

// вычисляем корень и выводим его

//float root = (a1 + b1) / 2;

//std::cout << "\nRoot = " << root << "\n";

isEnd = true;

}

else {

a0 = a1;

b0 = b1;

++k;

}

}

//std::cout << "---+-----------+-----------+-----------+-----------+------------+-----------+-----------+\n";

x_max = (a0 + b0) / 2;

std::cout << "\nРезультат вычисления методом «Фибоначчи»\n";

std::cout << "Максимальное значение функции на [" << a << ", " << b << "]\n";

std::cout << "y = " << f(x_max) << " точка максимума x = " << x_max << "\n";

std::cout << "Количество шагов = " << steps_max << "\n";

}

std::cout << "Время работы программы = " << clock() / 1000.0 << "\n";

}

// Метод «Пассивного поиска»

void LinearSearch(float a, float b, float eps)

{

float x, y, x_min, x_max, h = eps, min = 0, max = 0;

srand(time(0)); // начальное время

int step = 0;

if ((a <= -2 && b <= -2) || (a >= 0 && b >= 0))

{

for (x = a; x <= b; x += h)

{

step++;

y = f(x);

//std::cout << "|" << std::setw(8) << step << " |"

// << std::setw(10) << x << " |"

// << std::setw(10) << y << " |" << std::endl;

if (y < min || x == a)

{

min = y;

x_min = x;

}

}

//std::cout << "+---------+-----------+-----------+\n";

std::cout << "\nРезультат вычисления методом «Пассивного поиска»\n";

std::cout << "Минимальное значение функции на [" << a << ", " << b << "]\n";

std::cout << "y = " << f(x_min) << " точка минимума x = " << x_min << "\n";

std::cout << "Количество шагов = " << step << "\n";

}

else

{

for (x = a; x <= b; x += h)

{

step++;

y = f(x);

//std::cout << "|" << std::setw(8) << step << " |"

// << std::setw(10) << x << " |"

// << std::setw(10) << y << " |" << std::endl;

if (y > max || x == a)

{

max = y;

x_max = x;

}

}

//std::cout << "+---------+-----------+-----------+\n";

std::cout << "\nРезультат вычисления методом «Пассивного поиска»\n";

std::cout << "Максимальное значение функции на [" << a << ", " << b << "]\n";

std::cout << "y = " << f(x_max) << " точка максимума x = " << x_max << "\n";

std::cout << "Количество шагов = " << step << "\n";

}

std::cout << "Время работы программы = " << clock() / 1000.0 << "\n";

}

int main()

{

SetConsoleCP(1251);

SetConsoleOutputCP(1251);

float a, b, eps;

// ввод с клавиатуры начала и конца отрезка, эпсилона

std::cout << "Введите границы интервала [a, b]\n";

std::cout << "a = "; std::cin >> a;

std::cout << "b = "; std::cin >> b;

std::cout << "Введите значение точности, epsilon = ";

std::cin >> eps;

Bisection(a, b, eps);

GoldenSection(a, b, eps);

FibonacciSection(a, b, eps);

LinearSearch(a, b, eps);

system("Pause");

return 0;

}

Результат:

Отрезок [-5;-3]

Отрезок [-3;0]

Отрезок [0;5]

Выводы: были разработаны пользовательские алгоритмы для нахождения локального минимума (максимума) функции на заданном интервале одним из трёх способов. Также были приобретены навыки и умения анализа алгоритма, а ещё стало известно, что метод «деления отрезка» пополам выигрывает по времени работы программы.