Загрузка данных


import sympy as sp

x = sp.symbols("x")
f_expr = 2 * sp.log(x) - x ** 2 + 5

SEGMENTS = [(0.01, 0.1), (2, 3)]                        # отрезки изоляции корней (из графика)
ROOTS_COUNT = 2
EPSILON = 0.0001
PARTS = 1000

if ROOTS_COUNT > len(SEGMENTS):
    raise ValueError(f"В уравнении корней: {len(SEGMENTS)}, а запрошено {ROOTS_COUNT}")

f = sp.lambdify(x, f_expr, "math")
f1 = sp.lambdify(x, sp.diff(f_expr, x), "math")
f2 = sp.lambdify(x, sp.diff(f_expr, x, 2), "math")


def min_max(g, A, B):
    points = [A + (B - A) * k / PARTS for k in range(PARTS + 1)]
    values = [abs(g(p)) for p in points]
    return min(values), max(values)


def start_point(A, B):                                  # конец отрезка, где f(x) * f''(x) > 0
    if f(A) * f2(A) > 0:
        return A, B
    return B, A


def dichotomy(A, B):
    left, right = A, B
    value_left = f(left)

    if value_left * f(right) > 0:                       # проверка знаков до цикла
        return None

    previous_x = None
    iterations = 0
    while True:
        x_new = (left + right) / 2
        value_x = f(x_new)
        iterations += 1

        if value_x == 0:
            break
        if previous_x is not None and abs(x_new - previous_x) < EPSILON:
            break

        if value_left * value_x < 0:
            right = x_new
        else:
            left = x_new
            value_left = value_x
        previous_x = x_new

    return x_new, iterations


def simple_iterations(A, B):
    M1 = min_max(f1, A, B)[1]
    lam = -1 / M1 if f1(A) > 0 else 1 / M1              # параметр lambda: -1/M1 при f' > 0, 1/M1 при f' < 0
    phi = lambda t: t + lam * f(t)                      # x = phi(x) = x + lambda * f(x)
    q = min_max(lambda t: 1 + lam * f1(t), A, B)[1]     # q = max |phi'(x)| = max |1 + lambda * f'(x)|

    if q >= 1:                                          # при q >= 1 метод не сходится
        return None

    x_prev = (A + B) / 2
    iterations = 0
    while True:
        x_new = phi(x_prev)
        iterations += 1
        if q / (1 - q) * abs(x_new - x_prev) < EPSILON:
            break
        x_prev = x_new

    return x_new, iterations


def newton(A, B):
    m1 = min_max(f1, A, B)[0]
    M2 = min_max(f2, A, B)[1]

    x_prev = start_point(A, B)[0]
    iterations = 0
    while True:
        x_new = x_prev - f(x_prev) / f1(x_prev)
        iterations += 1
        if f(x_new) == 0:
            break
        if M2 / (2 * m1) * (x_new - x_prev) ** 2 < EPSILON:
            break
        x_prev = x_new

    return x_new, iterations


def secant(A, B):
    m1, M1 = min_max(f1, A, B)

    start, other = start_point(A, B)
    x_prev, x_cur = other, start
    iterations = 0
    while True:
        x_new = x_cur - f(x_cur) * (x_cur - x_prev) / (f(x_cur) - f(x_prev))
        iterations += 1
        if f(x_new) == 0:
            break
        if (M1 - m1) / m1 * abs(x_new - x_cur) < EPSILON:
            break
        x_prev, x_cur = x_cur, x_new

    return x_new, iterations


def chord(A, B):
    m1, M1 = min_max(f1, A, B)

    fixed, x_cur = start_point(A, B)                    # fixed - неподвижный конец хорды
    iterations = 0
    while True:
        x_new = x_cur - f(x_cur) * (fixed - x_cur) / (f(fixed) - f(x_cur))
        iterations += 1
        if f(x_new) == 0:
            break
        if (M1 - m1) / m1 * abs(x_new - x_cur) < EPSILON:
            break
        x_cur = x_new

    return x_new, iterations


def show(name, result):
    if result is None:
        print(f"  {name}: метод неприменим на этом отрезке")
    else:
        print(f"  {name}: {result[0]:.8f}, итераций: {result[1]}")


for i in range(ROOTS_COUNT):
    A, B = SEGMENTS[i]
    print(f"Корень {i + 1}, отрезок [{A}; {B}]")
    show("Дихотомия       ", dichotomy(A, B))
    show("Простые итерации", simple_iterations(A, B))
    show("Ньютон          ", newton(A, B))
    show("Секущие         ", secant(A, B))
    show("Хорды           ", chord(A, B))