- •Описание алгоритмов
- •1.1. Метод золотого сечения
- •1.2. Метод Фибоначчи
- •Задаём начальный интервал и точность .
- •Описание программы
- •Сравнительный анализ алгоритмов
- •3.1. Пример работы метода золотого сечения
- •3.2. Пример работы метода Фибоначчи
- •Приложение. Листинг программных модулей
- •3.3 Сравнение алгоритмов.
3.3 Сравнение алгоритмов.
Для сравнения эффективности двух численных методов одномерной оптимизации: метода золотого сечения и метода Фибоначчи был проведён ряд прогонов программы для различных значений точности. На рисунке 1 представлена зависимость времени выполнения алгоритмов от точности.
Рисунок 1. Сравнение методов одномерной оптимизации
Как видно из Рисунка 1, время выполнения метода золотого сечения практически не зависит от точности и составляет около 0.3–0.4 мкс. Метод Фибоначчи, напротив, показывает устойчивый рост времени с увеличением точности (от 0.8 до 1.7 мкс). Это объясняется необходимостью предварительного определения числа и испол ьзования более сложных операций с числами Фибоначчи на каждой итерации.
При малых точностях (до
)
метод Фибоначчи работает медленнее
золотого сечения в 2–3 раза. При высоких
точностях (
–
)
разница сокращается до 4–5 раз в пользу
золотого сечения, но абсолютное время
остаётся малым (менее 2 мкс).
Таким образом, метод золотого сечения оказывается предпочтительнее для практических задач благодаря простоте реализации и стабильно малому времени выполнения. Метод Фибоначчи, будучи теоретически оптимальным по числу итераций, проигрывает из-за вычислительных накладных расходов в данной программной реализации.
Выводы
В ходе выполнения лабораторной работы были достигнуты следующие результаты:
Изучены два классических метода одномерной оптимизации: золотого сечения и Фибоначчи. Рассмотрены их теоретические основы, алгоритмы и оценки скорости сходимости.
Разработана программа на языке C++, реализующая оба метода и позволяющая проводить автоматизированное сравнение их эффективности.
Экспериментально получены зависимости времени выполнения и количества итераций от требуемой точности.
Установлено, что метод золотого сечения работает значительно быстрее метода Фибоначчи во всём диапазоне исследованных точностей благодаря более простым вычислениям на каждой итерации.
Таким образом, цель работы достигнута: алгоритмы реализованы, их характеристики исследованы, полученные результаты сопоставлены с теорией.
Приложение. Листинг программных модулей
#include <iostream>
#include <vector>
#include <cmath>
#include <chrono>
#include <iomanip>
#include <windows.h>
using namespace std;
using namespace chrono;
double func(double x) {
return (x - 0.5) * (x - 0.5);
}
double goldenSection(double a, double b, double eps, int &iter) {
const double phi = (1.0 + sqrt(5.0)) / 2.0;
double x1 = b - (b - a) / phi;
double x2 = a + (b - a) / phi;
double f1 = func(x1);
double f2 = func(x2);
iter = 1;
while (b - a > eps) {
if (f1 < f2) {
b = x2;
x2 = x1;
x1 = b - (b - a) / phi;
f2 = f1;
f1 = func(x1);
} else {
a = x1;
x1 = x2;
x2 = a + (b - a) / phi;
f1 = f2;
f2 = func(x2);
}
++iter;
}
return (a + b) / 2.0;
}
// Предвычисление чисел Фибоначчи до n
vector<unsigned long long> precomputeFibonacci(int n) {
vector<unsigned long long> fib(n);
fib[0] = 1;
fib[1] = 1;
for (int i = 2; i < n; ++i)
fib[i] = fib[i-1] + fib[i-2];
return fib;
}
// Метод Фибоначчи с готовым массивом чисел
double fibonacciMethod(double a, double b, double eps, const vector<unsigned long long>& fib, int &iter) {
double L0 = b - a;
double required = L0 / eps;
int N = 2;
while (fib[N-1] < required) ++N; // индексация с 0, fib[N-1] = F_N
// Основной цикл метода Фибоначчи
double x1, x2, f1, f2;
for (int k = 1; k <= N-2; ++k) {
double ratio = (double)fib[N - k - 2] / fib[N - k];
x1 = a + ratio * (b - a);
x2 = b - ratio * (b - a);
f1 = func(x1);
f2 = func(x2);
if (f1 < f2) {
b = x2;
} else if (f1 > f2) {
a = x1;
} else {
a = x1;
b = x2;
}
}
iter = N - 2;
return (a + b) / 2.0;
}
int main() {
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
const double A = 0.0, B = 1.0;
const int REPEATS = 100000; // Увеличено для стабильности
vector<double> epsilons = {1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6};
// Предвычисляем числа Фибоначчи для максимальной требуемой точности
// Для eps=1e-6 требуется (B-A)/eps = 1e6, число Фибоначчи около 30, но запас
auto fib = precomputeFibonacci(50);
cout << fixed << setprecision(6);
cout << "Точность ε\tЗолотое сечение (мкс)\tИтераций\tФибоначчи (мкс)\tИтераций\n";
for (double eps : epsilons) {
// Измеряем overhead пустого цикла
auto start = high_resolution_clock::now();
for (int i = 0; i < REPEATS; ++i) {
volatile int dummy = 0;
(void)dummy;
}
auto end = high_resolution_clock::now();
double overhead = duration<double, micro>(end - start).count();
// Золотое сечение
long long totalIterGolden = 0;
start = high_resolution_clock::now();
for (int i = 0; i < REPEATS; ++i) {
int iter;
double x = goldenSection(A, B, eps, iter);
totalIterGolden += iter;
}
end = high_resolution_clock::now();
double timeGolden = (duration<double, micro>(end - start).count() - overhead) / REPEATS;
double avgIterGolden = static_cast<double>(totalIterGolden) / REPEATS;
// Фибоначчи
long long totalIterFib = 0;
start = high_resolution_clock::now();
for (int i = 0; i < REPEATS; ++i) {
int iter;
double x = fibonacciMethod(A, B, eps, fib, iter);
totalIterFib += iter;
}
end = high_resolution_clock::now();
double timeFib = (duration<double, micro>(end - start).count() - overhead) / REPEATS;
double avgIterFib = static_cast<double>(totalIterFib) / REPEATS;
// Вывод
cout << scientific << eps << "\t"
<< fixed << setprecision(2) << timeGolden << "\t\t\t"
<< avgIterGolden << "\t\t"
<< timeFib << "\t\t"
<< avgIterFib << "\n";
}
return 0;
}
