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

Этот модуль предоставляет набор численных методов для решения нелинейных уравнений и задач оптимизации.  Он включает в себя как методы поиска корней уравнений (методы бисекции, Ньютона, секущих, Вегстейна, простой итерации), так и метод поиска минимума функции (метод дихотомии).

## Методы решения нелинейных уравнений

### 1. Метод бисекции (деления пополам)

**Описание:**

Метод бисекции — это простой и надежный метод нахождения корня непрерывной функции на заданном интервале. Он основан на теореме о промежуточном значении: если непрерывная функция `f(x)` меняет знак на интервале `[a, b]`, то есть `f(a) * f(b) < 0`, то на этом интервале существует хотя бы один корень уравнения `f(x) = 0`.

**Алгоритм:**

1. Проверить, что `f(a) * f(b) < 0`. Если это условие не выполняется, метод неприменим.
2. Вычислить середину интервала: `c = (a + b) / 2`.
3. Если `|f(c)| < eps` (где `eps` — заданная точность) или `|b - a| < eps`, то `c` является приближенным корнем.
4. Если `f(a) * f(c) < 0`, то корень находится на интервале `[a, c]`, иначе — на интервале `[c, b]`.
5. Повторить шаги 2-4 для нового интервала до достижения заданной точности или максимального числа итераций.

**Преимущества:**

* Гарантированная сходимость, если выполнены условия применимости.
* Простота реализации.

**Недостатки:**

* Относительно медленная сходимость (линейная).
* Требует предварительной локализации корня.

**Реализация:**

```python
def bisection_method(f, a, b, eps=1e-6, max_iter=1000):
    # ... (код метода)
```

**Пример использования:**

```python
f = lambda x: x**2 - 4
root = bisection_method(f, 1, 3)
print(f"Корень: {root}")  # Вывод: Корень: 2.0
```

### 2. Метод бисекции с параметрами

**Описание:**

Расширенная версия метода бисекции, позволяющая передавать дополнительные параметры в функцию `f(x)`.  Это полезно, когда функция зависит не только от `x`, но и от других известных величин.

**Алгоритм:**

Аналогичен обычному методу бисекции, но при вычислении значения функции используется `f(x, *params)`, где `params` — кортеж дополнительных параметров.

**Реализация:**

```python
def bisection_method_params(f, a, b, tol, params, max_iters=10000):
    # ... (код метода)
```

**Пример использования:**

```python
def equation(x, a, b):
    return a * x**2 + b

root = bisection_method_params(equation, 1.0, 3.0, tol=1e-6, params=(1, -4))
print(f"Корень: {root}")  # Вывод: Корень: 2.0
```

### 3. Метод Ньютона (касательных)

**Описание:**

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

**Алгоритм:**

1. Задать начальное приближение `x0`.
2. Вычислить `x_{n+1} = x_n - f(x_n) / f'(x_n)`, где `f'(x)` — производная функции `f(x)`.
3. Если `|f(x_{n+1})| < eps`, то `x_{n+1}` является приближенным корнем.
4. Повторить шаги 2-3 до достижения заданной точности или максимального числа итераций.

**Преимущества:**

* Быстрая сходимость (квадратичная) при хорошем начальном приближении.

**Недостатки:**

* Требует вычисления производной функции.
* Сходимость не гарантирована и зависит от выбора начального приближения.  Метод может расходиться или сходиться к другому корню.
* Может возникнуть деление на ноль, если производная в какой-то точке равна нулю.

**Реализация:**

```python
def newton_method(f, df, x0, eps=1e-6, max_iter=1000):
    # ... (код метода)
```

**Пример использования:**

```python
f = lambda x: x**2 - 2
df = lambda x: 2*x
root = newton_method(f, df, x0=1.5)
print(f"Корень: {root}")  # Вывод: Корень: 1.4142135623746899
```

### 4. Метод секущих

**Описание:**

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

**Алгоритм:**

1. Задать два начальных приближения `x0` и `x1`.
2. Вычислить `x_{n+2} = x_{n+1} - f(x_{n+1}) * (x_{n+1} - x_n) / (f(x_{n+1}) - f(x_n))`.
3. Если `|f(x_{n+2})| < eps`, то `x_{n+2}` является приближенным корнем.
4. Повторить шаги 2-3 до достижения заданной точности или максимального числа итераций.

**Преимущества:**

* Не требует вычисления производной.
* Сходимость обычно быстрее, чем у метода бисекции, хотя и медленнее, чем у метода Ньютона при хорошем начальном приближении.

**Недостатки:**

* Сходимость не гарантирована и зависит от выбора начальных приближений.
* Может возникнуть деление на ноль, если `f(x_{n+1}) = f(x_n)`.

**Реализация:**

```python
def secant_method(f, x0, x1, eps=1e-6, max_iter=1000):
    # ... (код метода)
```

**Пример использования:**

```python
f = lambda x: x**2 - 2
root = secant_method(f, x0=1, x1=2)
print(f"Корень: {root}")  # Вывод: Корень: 1.4142135620573204
```

### 5. Метод Вегстейна

**Описание:**

Метод Вегстейна — это метод нахождения неподвижной точки функции `g(x)`, то есть решения уравнения `x = g(x)`.  Он является модификацией метода простой итерации, использующей линейную экстраполяцию для ускорения сходимости.

**Алгоритм:**

1. Преобразовать уравнение `f(x) = 0` к виду `x = g(x)`.  Например, если `f(x) = x^2 - 2x - 3`, то можно взять `g(x) = sqrt(2x + 3)`.
2. Задать начальное приближение `x0`.
3. Вычислить `x_1 = g(x_0)`.
4. Для `n >= 1` вычислить:
   * `q = (g(x_n) - g(x_{n-1})) / ((x_n - x_{n-1}) - (g(x_n) - g(x_{n-1})))`
   * `x_{n+1} = (1 - q) * x_n + q * g(x_n)`
5. Если `|x_{n+1} - x_n| < tol`, то `x_{n+1}` является приближенным решением.
6. Повторить шаги 4-5 до достижения заданной точности или максимального числа итераций.

**Преимущества:**

* Потенциально более быстрая сходимость, чем у метода простой итерации.

**Недостатки:**

* Сходимость не гарантирована и зависит от выбора функции `g(x)` и начального приближения.
* Может возникнуть деление на ноль при вычислении `q`.

**Реализация:**

```python
def wegstein_method(g, x0, max_iter=100, tol=1e-6):
    # ... (код метода)
```

**Пример использования:**

```python
g = lambda x: x**0.5  # Решение x = sqrt(x)
root, history = wegstein_method(g, x0=2.0)
print(f"Корень: {root}")
```

### 6. Метод простой итерации

**Описание:**

Метод простой итерации также используется для нахождения неподвижной точки функции `g(x)`.  Он заключается в последовательном применении функции `g(x)` к текущему приближению.

**Алгоритм:**

1. Преобразовать уравнение `f(x) = 0` к виду `x = g(x)`.
2. Задать начальное приближение `x0`.
3. Вычислить `x_{n+1} = g(x_n)`.
4. Если `|x_{n+1} - x_n| < eps`, то `x_{n+1}` является приближенным решением.
5. Повторить шаги 3-4 до достижения заданной точности или максимального числа итераций.

**Преимущества:**

* Простота реализации.

**Недостатки:**

* Сходимость гарантирована только если функция `g(x)` является сжимающим отображением в окрестности корня, то есть `|g'(x)| < 1`.
* Сходимость может быть медленной (линейная).

**Реализация:**

```python
def simple_iteration_method(g, x0, eps=1e-6, max_iter=1000):
    # ... (код метода)
```

**Пример использования:**

```python
import math
g = lambda x: math.cos(x)  # Решение x = cos(x)
root = simple_iteration_method(g, x0=0.5)
print(f"Корень: {root}")
```

## Метод оптимизации

### 7. Метод дихотомии (для поиска минимума)

**Описание:**

Этот метод используется для нахождения минимума унимодальной функции на заданном интервале.  Унимодальная функция — это функция, имеющая на интервале только один локальный минимум (или максимум).  Метод дихотомии последовательно сужает интервал поиска, сравнивая значения функции в двух точках внутри интервала.

**Алгоритм:**

1. Задать начальный интервал `[a, b]`.
2. Вычислить середину интервала: `c = (a + b) / 2`.
3. Выбрать две точки внутри интервала, симметричные относительно `c`, например, `x1 = c - eps/2` и `x2 = c + eps/2`, где `eps` — небольшая величина, определяющая точность.
4. Сравнить значения функции:
   * Если `f(x1) < f(x2)`, то минимум находится на интервале `[a, c]`.
   * Иначе минимум находится на интервале `[c, b]`.
5. Обновить границы интервала в соответствии с результатом сравнения.
6. Повторить шаги 2-5 до достижения заданной точности (длина интервала `b - a < eps`) или максимального числа итераций.

**Преимущества:**

* Простота реализации.
* Гарантированная сходимость для унимодальных функций.

**Недостатки:**

* Требует, чтобы функция была унимодальной.
* Сходимость может быть медленной (линейная).

**Реализация:**

```python
def dichotomy_method(f, a, b, eps=1e-6, max_iter=1000):
    # ... (код метода)
```

**Пример использования:**

```python
f = lambda x: x**2 - 4*x - 1
minimum = dichotomy_method(f, 0, 5)
print(f"Минимум: {minimum}")  # Вывод: Минимум: 2.0000001788139343
```

## Заключение

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