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

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

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

### `simple_iteration_system(G, x0, eps=1e-6, max_iter=1000)`

Решает систему нелинейных уравнений методом простой итерации.

#### Теоретическая справка

Метод простой итерации основан на принципе сжимающего отображения. Если система уравнений может быть представлена в виде  `x = G(x)`, где `G(x)` — вектор-функция, и если `G(x)` является сжимающим отображением в окрестности решения (т.е., норма матрицы Якоби `||J_G(x)|| < 1`), то итерационная последовательность `x_{k+1} = G(x_k)` сходится к единственному решению.

#### Параметры

- `G` (callable): Вектор-функция, преобразующая систему уравнений к виду `x = G(x)`. Принимает `numpy.ndarray` формы `(n,)` и возвращает `numpy.ndarray` формы `(n,)`.
- `x0` (array_like): Начальное приближение к решению. Массив формы `(n,)` с вещественными числами.
- `eps` (float, optional): Допустимая погрешность решения (по евклидовой норме изменения переменных). По умолчанию `1e-6`.
- `max_iter` (int, optional): Максимальное количество итераций. По умолчанию `1000`.

#### Возвращаемое значение

- `numpy.ndarray`: Решение системы уравнений `x` в виде массива формы `(n,)`.

#### Пример

```python
import numpy as np
import math

def G(x):
    return np.array([0.7 - math.cos(x[1]), (2 - math.sin(x[0])) / 2])

x0 = [0.0, 0.0]
solution = simple_iteration_system(G, x0)
print(f"Решение: x ≈ {solution[0]:.6f}, y ≈ {solution[1]:.6f}")
# Ожидаемый вывод: Решение: x ≈ 0.113100, y ≈ 0.943571
```

#### Замечания

1. Необходимо привести исходную систему к виду `x = G(x)`.
2. Сходимость гарантируется, если `||J_G(x)|| < 1` в окрестности решения.
3. Используется NumPy для ускорения вычислений.
4. Критерий остановки: `||x_next - x||₂ < eps`.
5. Метод может расходиться при плохом начальном приближении или для плохо обусловленных систем.

### `simple_iteration_system_no_np(G, x0, eps=1e-6, max_iter=1000)`

Решает систему нелинейных уравнений методом простой итерации без использования NumPy.

#### Теоретическая справка

Аналогична `simple_iteration_system`, но реализована без использования библиотеки NumPy.

#### Параметры

- `G` (callable): Вектор-функция, преобразующая систему уравнений к виду `x = G(x)`. Принимает список `x` и возвращает список значений для обновления переменных.
- `x0` (list of float): Начальное приближение к решению. Список вещественных чисел длины `n`.
- `eps` (float, optional): Допустимая погрешность решения (по евклидовой норме изменения переменных). По умолчанию `1e-6`.
- `max_iter` (int, optional): Максимальное количество итераций. По умолчанию `1000`.

#### Возвращаемое значение

- `list of float`: Решение системы уравнений `x` в виде списка длины `n`.

#### Пример

```python
import math

def G(x):
    return [0.7 - math.cos(x[1]), (2 - math.sin(x[0])) / 2]

x0 = [0.0, 0.0]
solution = simple_iteration_system_no_np(G, x0)
print(f"Решение: x ≈ {solution[0]:.6f}, y ≈ {solution[1]:.6f}")
# Ожидаемый вывод: Решение: x ≈ 0.113100, y ≈ 0.943571
```

#### Замечания

Аналогичны `simple_iteration_system`, но без использования NumPy.

## 2. Метод Зейделя

### `seidel_method(g_funcs, x0, tol=1e-6, max_iter=100)`

Решает систему уравнений методом Гаусса-Зейделя.

#### Теоретическая справка

Метод Гаусса-Зейделя — это итерационный метод решения систем уравнений, где значения переменных обновляются последовательно внутри каждой итерации.  Система уравнений должна быть представлена в виде `x_i = g_i(x_1, x_2, ..., x_n)`.  В отличие от метода Якоби, где все переменные обновляются одновременно в конце итерации, метод Зейделя использует уже обновленные значения переменных в текущей итерации, что может ускорить сходимость.  Сходимость гарантирована для линейных систем, если матрица системы диагонально преобладающая или положительно определенная.

#### Параметры

- `g_funcs` (list of callable): Список функций, определяющих систему уравнений в виде `x_i = g_i(x_1, x_2, ..., x_n)`. Каждая функция принимает массив `x` и возвращает обновленное значение для `i`-й переменной.
- `x0` (array_like): Начальное приближение к решению. Массив формы `(n,)` с вещественными числами.
- `tol` (float, optional): Допустимая погрешность решения (по максимуму изменения переменных). По умолчанию `1e-6`.
- `max_iter` (int, optional): Максимальное количество итераций. По умолчанию `100`.

#### Возвращаемое значение

- `numpy.ndarray`: Решение системы уравнений `x` в виде массива формы `(n,)`.

#### Пример

```python
def g1(x): return (7 - x[1] + x[2])/3
def g2(x): return (8 - 2*x[0] - x[2])/4
def g3(x): return (9 - 3*x[0] - 2*x[1])/5

x0 = [0.0, 0.0, 0.0]
solution = seidel_method([g1, g2, g3], x0)
print(f"Решение: x ≈ {solution[0]:.4f}, y ≈ {solution[1]:.4f}, z ≈ {solution[2]:.4f}")
# Ожидаемый вывод: Решение: x ≈ 2.0909, y ≈ 0.9091, z ≈ 0.1818
```

#### Замечания

1. Необходимо привести систему к виду `x_i = g_i(x)`.
2. Сходимость гарантирована для диагонально преобладающих или положительно определенных матриц.
3. Использует обновленные значения переменных внутри итерации.
4. Критерий остановки: максимальное изменение переменной за итерацию меньше `tol`.

## 3. Метод Ньютона для систем (2x2)

### `newton_system(F, J, x0, tol=1e-6, max_iter=100)`

Решает систему нелинейных уравнений методом Ньютона для систем 2x2, используя правило Крамера для решения линейных подсистем.

#### Теоретическая справка

Метод Ньютона для систем нелинейных уравнений `F(x) = 0` использует итерационную формулу `x_{k+1} = x_k + Δx_k`, где `Δx_k` является решением линеаризованной системы `J(x_k) * Δx_k = -F(x_k)`.  `J(x)` — матрица Якоби системы.  Для систем 2x2 решение `Δx_k` может быть найдено с помощью правила Крамера.  Сходимость гарантируется при хорошем начальном приближении и невырожденной матрице Якоби.

#### Параметры

- `F` (callable): Вектор-функция, возвращающая невязки системы уравнений в виде списка или массива. Принимает 1-D массив `x` и возвращает 1-D массив значений функций.
- `J` (callable): Матрица Якоби системы, возвращающая 2x2 массив частных производных. Принимает 1-D массив `x` и возвращает 2x2 массив значений производных.
- `x0` (array_like): Начальное приближение к решению. Массив формы `(2,)` с двумя вещественными числами.
- `tol` (float, optional): Допустимая погрешность решения (по евклидовой норме невязки). По умолчанию `1e-6`.
- `max_iter` (int, optional): Максимальное количество итераций. По умолчанию `100`.

#### Возвращаемое значение

- `numpy.ndarray`: Решение системы уравнений `x` в виде массива формы `(2,)`.

#### Пример

```python
def F(x):
    return [x[0]**2 + x[1]**2 - 1, x[1] - x[0]**2 + 0.5]

def J(x):
    return [[2*x[0], 2*x[1]], [-2*x[0], 1]]

x0 = [1.0, 1.0]
solution = newton_system(F, J, x0)
print(f"Решение: x ≈ {solution[0]:.4f}, y ≈ {solution[1]:.4f}")
# Ожидаемый вывод: Решение: x ≈ 0.8774, y ≈ 0.5000
```

#### Замечания

1. Матрица Якоби должна быть невырожденной.
2. Использует правило Крамера для решения 2x2 СЛАУ.
3. Сходимость зависит от начального приближения.
4. Проверка вырожденности матрицы Якоби: `abs(det(J)) < 1e-12`.
5. Критерий остановки: `||F(x)||₂ < tol`.

## 4. Метод Гаусса (с Numba)

### `solve_gauss_njit(A, b, FLOAT_TOLERANCE=1e-12)`

Решает систему линейных уравнений `Ax = b` методом Гаусса с частичным выбором ведущего элемента, используя Numba для ускорения.

#### Теоретическая справка

Метод Гаусса — это прямой метод решения систем линейных уравнений, основанный на приведении расширенной матрицы `[A|b]` к верхнетреугольному виду с помощью элементарных преобразований строк.  Частичный выбор ведущего элемента (перестановка строк) используется для обеспечения численной устойчивости, минимизируя ошибки округления.  Алгоритм состоит из прямого хода (приведение к верхнетреугольному виду) и обратного хода (нахождение решения).

#### Параметры

- `A` (`np.ndarray`, shape `(n, n)`): Квадратная матрица коэффициентов системы уравнений.
- `b` (`np.ndarray`, shape `(n,)`): Вектор свободных членов системы уравнений.
- `FLOAT_TOLERANCE` (float, optional): Пороговое значение для проверки вырожденности матрицы. По умолчанию `1e-12`.

#### Возвращаемое значение

- `np.ndarray`, shape `(n,)`: Вектор решения системы уравнений. Если матрица `A` вырожденная, возвращается массив, заполненный `np.nan`.

#### Пример

```python
import numpy as np

# Пример 1: Диагональная матрица
A = np.diag([1, 2, 3])
b = [1, 4, 9]
print(solve_gauss_njit(A, b))  # Ожидаемый вывод: [1., 2., 3.]

# Пример 2: Вырожденная матрица
A = np.array([[1, 2], [2, 4]])
b = [3, 6]
print(solve_gauss_njit(A, b))  # Ожидаемый вывод: [nan, nan]

# Пример 3: Система с неточными данными
A = np.array([[0.001, 1], [1, 0.001]])
b = [2.001, 2.001]
print(solve_gauss_njit(A, b))  # Ожидаемый вывод: [1., 2.]
```

#### Замечания

1. Матрица `A` должна быть квадратной.
2. Частичный выбор ведущего элемента повышает численную устойчивость.
3. `FLOAT_TOLERANCE` определяет порог для вырожденности матрицы.
4. Использует Numba (`@njit`) для значительного ускорения вычислений.

## 5. Список всех методов

`SISTEMS = [simple_iteration_system, simple_iteration_system_no_np, seidel_method, newton_system, solve_gauss_njit]`

Этот список содержит все реализованные методы решения систем уравнений.
