commit 61cc4726691ce25f55e93aafb1dcb5cbf5b7cfda Author: Arity-T Date: Fri Jan 2 13:33:31 2026 +0300 task1 diff --git a/task1/golden_section_search.py b/task1/golden_section_search.py new file mode 100644 index 0000000..c7f9a23 --- /dev/null +++ b/task1/golden_section_search.py @@ -0,0 +1,167 @@ +# golden_section_search.py +# Метод золотого сечения для одномерной оптимизации (10 итераций) +# Условия из задачи: f(x) = sqrt(x^2 + 9) / 4 + (5 - x) / 5, X = [-3, 8] + +import math +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np + +# Глобальный счётчик обращений к оракулу +oracle_calls = 0 + + +def f(x: float) -> float: + global oracle_calls + oracle_calls += 1 + return math.sqrt(x**2 + 9) / 4 + (5 - x) / 5 + + +# Параметры +a_init = -3.0 +b_init = 8.0 +num_iters = 10 # ровно 10 итераций + +# Константы золотого сечения +phi = (1 + math.sqrt(5)) / 2.0 # ~1.618... +r = 1 / phi # ~0.618... +c = 1 - r # ~0.382... + +# Создаём папку для графиков +Path("golden_section_plots").mkdir(exist_ok=True) + +# История итераций +history = [] + +# Начальные значения +a = a_init +b = b_init + +# Начальные точки +y = a + c * (b - a) +z = a + r * (b - a) +fy = f(y) +fz = f(z) + +print("Метод золотого сечения") +print("=" * 80) + +print("\nНачальное состояние:") +print(f" Интервал: [{a:.6f}, {b:.6f}], длина = {b - a:.6f}") +print(f" y = {y:.6f}, f(y) = {fy:.6f}") +print(f" z = {z:.6f}, f(z) = {fz:.6f}") + +# Основной цикл +for k in range(1, num_iters + 1): + # Сохраняем состояние до обновления + history.append( + { + "iter": k, + "a": a, + "b": b, + "y": y, + "z": z, + "fy": fy, + "fz": fz, + "interval_length": b - a, + } + ) + + print(f"\nИтерация {k}:") + print(f" Интервал: [{a:.6f}, {b:.6f}], длина = {b - a:.6f}") + print(f" y = {y:.6f}, f(y) = {fy:.6f}") + print(f" z = {z:.6f}, f(z) = {fz:.6f}") + + # Построение графика + x_plot = np.linspace(a_init, b_init, 500) + y_plot = [f(x) for x in x_plot] + + plt.figure(figsize=(10, 6)) + plt.plot(x_plot, y_plot, "b-", linewidth=2, label="f(x)") + plt.axvline( + a, color="red", linestyle="--", alpha=0.7, label=f"Интервал [{a:.3f}, {b:.3f}]" + ) + plt.axvline(b, color="red", linestyle="--", alpha=0.7) + plt.plot(y, fy, "go", markersize=10, label=f"y = {y:.3f}, f(y) = {fy:.4f}") + plt.plot(z, fz, "mo", markersize=10, label=f"z = {z:.3f}, f(z) = {fz:.4f}") + + plt.xlabel("x", fontsize=12) + plt.ylabel("f(x)", fontsize=12) + plt.title(f"Золотое сечение - Итерация {k}", fontsize=14, fontweight="bold") + plt.legend(fontsize=10) + plt.grid(True, alpha=0.3) + plt.tight_layout() + plt.savefig(f"golden_section_plots/iteration_{k:02d}.png", dpi=150) + plt.close() + + # Обновление интервала + if fy <= fz: + print(f" f(y) <= f(z) -> новый интервал: [{a:.6f}, {z:.6f}]") + b = z + z = y + fz = fy + y = a + c * (b - a) + fy = f(y) + else: + print(f" f(y) > f(z) -> новый интервал: [{y:.6f}, {b:.6f}]") + a = y + y = z + fy = fz + z = a + r * (b - a) + fz = f(z) + +# Итоговая оценка минимума +x_star = (a + b) / 2.0 +f_star = f(x_star) + +# Финальный график +x_plot = np.linspace(a_init, b_init, 500) +y_plot = [f(x) for x in x_plot] + +plt.figure(figsize=(10, 6)) +plt.plot(x_plot, y_plot, "b-", linewidth=2, label="f(x)") +plt.axvline( + a, + color="red", + linestyle="--", + alpha=0.7, + label=f"Финальный интервал [{a:.6f}, {b:.6f}]", +) +plt.axvline(b, color="red", linestyle="--", alpha=0.7) +plt.plot( + x_star, + f_star, + "r*", + markersize=20, + label=f"x* = {x_star:.6f}, f(x*) = {f_star:.6f}", +) + +plt.xlabel("x", fontsize=12) +plt.ylabel("f(x)", fontsize=12) +plt.title("Золотое сечение - Финальный результат", fontsize=14, fontweight="bold") +plt.legend(fontsize=10) +plt.grid(True, alpha=0.3) +plt.tight_layout() +plt.savefig("golden_section_plots/final_result.png", dpi=150) +plt.close() + +print("\n" + "=" * 80) +print(f"\nИтераций выполнено: {num_iters}") +print(f"x* ~= {x_star:.6f}") +print(f"f(x*) ~= {f_star:.6f}") +print(f"Длина финального интервала: {b - a:.6f}") +# print(f"\nОбращений к оракулу (вычислений функции): {oracle_calls}") +print("\nГрафики сохранены в папке 'golden_section_plots/'") + +# Таблица всех итераций +print("\n" + "=" * 80) +print("Сводная таблица:") +print("-" * 80) +for row in history: + print( + f"Итерация {row['iter']:2d}: " + f"[{row['a']:7.4f}, {row['b']:7.4f}] (D={row['interval_length']:7.4f}), " + f"y={row['y']:7.4f} (f={row['fy']:.4f}), " + f"z={row['z']:7.4f} (f={row['fz']:.4f})" + ) diff --git a/task1/uniform_search.py b/task1/uniform_search.py new file mode 100644 index 0000000..81b76e7 --- /dev/null +++ b/task1/uniform_search.py @@ -0,0 +1,255 @@ +"""Лекция 4 (стр. 15) + +Задача. Найти пару {x*, f(x*)} для целевой функции вида + f(x) = sqrt(x^2 + 9) / 4 + (5 - x) / 5 +при условии, что X = [-3, 8]. +Взять N = 10, eps_x = 0.05, eps_f = 0.001. +Применить метод равномерного поиска. +""" + +import math +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np + +# Глобальный счётчик обращений к оракулу +oracle_calls = 0 + + +def f(x: float) -> float: + global oracle_calls + oracle_calls += 1 + return math.sqrt(x**2 + 9) / 4 + (5 - x) / 5 + + +a = -3 +b = 8 +N = 10 +eps_x = 0.05 +eps_f = 0.001 + +""" +Количество итераций может быть найдено из условия: +(2/N)^k * (b - a) / 2 <= eps_x +""" +print("Метод равномерного поиска") +print("=" * 80) +print( + f"Расчётное количество итераций: {math.ceil(math.log(2 * eps_x / (b - a), 2 / N))}" +) + +# Создаём папку для графиков +Path("uniform_search_plots").mkdir(exist_ok=True) + +# Сохраняем начальные значения для графиков +a_init = a +b_init = b + +# Метод равномерного поиска +k = 0 +a_k, b_k = a, b +prev_best_f = None +best_x = None +best_fx = None +history = [] # для отладки/анализа + +print("\nНачальный интервал: [{:.6f}, {:.6f}]".format(a_k, b_k)) +print("-" * 80) + +while True: + k += 1 + # Разбиваем [a_k, b_k] на N равных частей + Delta = (b_k - a_k) / N + xs = [a_k + i * Delta for i in range(N + 1)] # включая концы + # Считаем значения в узлах x_1..x_{N-1} + vals = [f(x) for x in xs] + # Ищем минимум среди внутренних узлов (по лекции) + i_min = min(range(1, N), key=lambda i: vals[i]) + x_min = xs[i_min] + fx_min = vals[i_min] + + # Сжимаем интервал к соседям минимума (x_{i-1}, x_{i+1}) + a_next = xs[i_min - 1] + b_next = xs[i_min + 1] + + # Обновляем лучшее известное + best_x = x_min + best_fx = fx_min + + # Критерии остановки (из лекции): по точности по x и стабилизации по f + Delta_next = b_next - a_next # длина нового интервала + center_next = (a_next + b_next) / 2 # удобная оценка x* + fx_center_next = f(center_next) + + # По лекции можно использовать центр нового интервала как текущую оценку x* + # и/или минимальный узел — обе оценки допустимы. Здесь — центр интервала, + # чтобы гарантировать |x - x*| <= Delta_next/2. + approx_x = center_next + approx_fx = fx_center_next + + if prev_best_f is None: + prev_best_f = approx_fx + + # Условия остановки + stop_by_x = (Delta_next / 2) <= eps_x + stop_by_f = abs(approx_fx - prev_best_f) <= eps_f + + history.append( + { + "iter": k, + "a_k": a_k, + "b_k": b_k, + "i_min": i_min, + "x_min": x_min, + "f(x_min)": fx_min, + "a_next": a_next, + "b_next": b_next, + "Delta_next/2": Delta_next / 2, + "approx_x": approx_x, + "approx_fx": approx_fx, + "xs": xs.copy(), + "vals": vals.copy(), + # "stop_by_x": stop_by_x, + # "stop_by_f": stop_by_f, + } + ) + + # Построение графика для текущей итерации + x_plot = np.linspace(a_init, b_init, 500) + y_plot = [f(x) for x in x_plot] + + plt.figure(figsize=(12, 7)) + plt.plot(x_plot, y_plot, "b-", linewidth=2, label="f(x)") + + # Текущий интервал + plt.axvline(a_k, color="red", linestyle="--", alpha=0.7, linewidth=1.5) + plt.axvline(b_k, color="red", linestyle="--", alpha=0.7, linewidth=1.5) + plt.axvspan( + a_k, + b_k, + alpha=0.1, + color="red", + label=f"Текущий интервал [{a_k:.3f}, {b_k:.3f}]", + ) + + # Точки разбиения + plt.plot( + xs, vals, "o", color="orange", markersize=6, alpha=0.6, label="Узлы разбиения" + ) + + # Минимальная точка + plt.plot( + x_min, + fx_min, + "go", + markersize=12, + label=f"Минимум: x={x_min:.4f}, f(x)={fx_min:.4f}", + zorder=5, + ) + + # Следующий интервал + plt.axvspan( + a_next, + b_next, + alpha=0.15, + color="green", + label=f"Новый интервал [{a_next:.3f}, {b_next:.3f}]", + ) + + plt.xlabel("x", fontsize=12) + plt.ylabel("f(x)", fontsize=12) + plt.title( + f"Равномерный поиск - Итерация {k}\nN={N}, D={Delta:.4f}, D_next/2={Delta_next / 2:.4f}", + fontsize=13, + fontweight="bold", + ) + plt.legend(fontsize=9, loc="best") + plt.grid(True, alpha=0.3) + plt.tight_layout() + plt.savefig(f"uniform_search_plots/iteration_{k:02d}.png", dpi=150) + plt.close() + + # Вывод информации по итерации + print(f"\nИтерация {k}:") + print(f" Интервал: [{a_k:.6f}, {b_k:.6f}], D = {Delta:.6f}") + print(f" Минимум найден в узле i={i_min}: x = {x_min:.6f}, f(x) = {fx_min:.6f}") + print( + f" Новый интервал: [{a_next:.6f}, {b_next:.6f}], D_next/2 = {Delta_next / 2:.6f}" + ) + print(f" Аппроксимация: x ~= {approx_x:.6f}, f(x) ~= {approx_fx:.6f}") + print(f" Критерии: D/2 <= eps_x? {stop_by_x}, |Df| <= eps_f? {stop_by_f}") + + # Переход на следующую итерацию + a_k, b_k = a_next, b_next + prev_best_f = approx_fx + + if stop_by_x and stop_by_f: + break + + # Дополнительная защита: если интервал перестал уменьшаться (численная стабильность) + if Delta_next <= 1e-12: + break + +# Финальный ответ +x_star = approx_x +f_star = approx_fx + +# Финальный график +x_plot = np.linspace(a_init, b_init, 500) +y_plot = [f(x) for x in x_plot] + +plt.figure(figsize=(12, 7)) +plt.plot(x_plot, y_plot, "b-", linewidth=2, label="f(x)") +plt.axvline(a_k, color="red", linestyle="--", alpha=0.7, linewidth=1.5) +plt.axvline(b_k, color="red", linestyle="--", alpha=0.7, linewidth=1.5) +plt.axvspan( + a_k, + b_k, + alpha=0.15, + color="red", + label=f"Финальный интервал [{a_k:.6f}, {b_k:.6f}]", +) +plt.plot( + x_star, + f_star, + "r*", + markersize=20, + label=f"x* = {x_star:.6f}, f(x*) = {f_star:.6f}", + zorder=5, +) + +plt.xlabel("x", fontsize=12) +plt.ylabel("f(x)", fontsize=12) +plt.title( + f"Равномерный поиск - Финальный результат\nВыполнено итераций: {k}", + fontsize=14, + fontweight="bold", +) +plt.legend(fontsize=10, loc="best") +plt.grid(True, alpha=0.3) +plt.tight_layout() +plt.savefig("uniform_search_plots/final_result.png", dpi=150) +plt.close() + +print("\n" + "=" * 80) +print(f"\nИтераций выполнено: {k}") +print(f"x* ~= {x_star:.6f}") +print(f"f(x*) ~= {f_star:.6f}") +print(f"Длина конечного интервала / 2 <= eps_x? {(b_k - a_k) / 2 <= eps_x}") +print(f"|Df| <= eps_f? {abs(approx_fx - prev_best_f) <= eps_f}") +# print(f"\nОбращений к оракулу (вычислений функции): {oracle_calls}") +print("\nГрафики сохранены в папке 'uniform_search_plots/'") + +# При желании выведем краткую таблицу нескольких последних итераций +print("\n" + "=" * 80) +print("Сводная таблица:") +print("-" * 80) +for row in history: + print( + f"Итер {row['iter']:2d}: " + f"[{row['a_k']:7.4f}, {row['b_k']:7.4f}] -> " + f"x_min={row['x_min']:7.4f} (f={row['f(x_min)']:.4f}) -> " + f"[{row['a_next']:7.4f}, {row['b_next']:7.4f}], " + f"D/2={row['Delta_next/2']:.4f}" + )