
ФЕДЕРАЛЬНОЕ АГЕНТСТВО СВЯЗИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ
«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М.А. БОНЧ-БРУЕВИЧА»
(СПбГУТ)
Кафедра безопасности информационных систем
ОТЧЁТ
по итоговой работе №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]
Выводы: были разработаны пользовательские алгоритмы для нахождения локального минимума (максимума) функции на заданном интервале одним из трёх способов. Также были приобретены навыки и умения анализа алгоритма, а ещё стало известно, что метод «деления отрезка» пополам выигрывает по времени работы программы.