E129. Метод Ньютона (касательных) для поиска корней

e-maxx algorithm original: C/C++ #algorithm #emaxx #math #numerical-methods #search
선택한 UI 언어에 맞게 문제 텍스트를 러시아어에서 번역합니다. 코드는 변경하지 않습니다.

Источник: e-maxx.ru/algo, страница PDF 420.

Это итерационный метод, изобретённый Исааком Ньютоном (Isaak Newton) около 1664 г. Впрочем, иногда этот метод называют методом Ньютона-Рафсона (Raphson), поскольку Рафсон изобрёл тот же самый 알고리즘 на несколько лет позже Ньютона, однако его статья была опубликована намного раньше. 문제 заключается в следующем. given уравнение: it is required решить это уравнение, точнее, find один из его корней (предполагается, что корень

существует). Предполагается, что

непрерывна и дифференцируема на отрезке

.

알고리즘

입력ным параметром 알고리즘а, кроме функции

, является также начальное приближение —

некоторое

, от которого 알고리즘 начинает идти.

Пусть уже вычислено

, вычислим

следующим образом. Проведём касательную к 그래프ику функции

в

точке

, и найдём точку пересечения этой касательной с осью абсцисс.

положим равным найденной точке,

и повторим весь процесс с начала. Нетрудно получить следующую формулу:

Интуитивно ясно, что если функция

достаточно "хорошая" (гладкая), а

находится достаточно близко от корня,

то

будет находиться ещё ближе к искомому корню. Скорость сходимости является квадратичной, что, условно говоря, означает, что number точных разрядов

в приближенном значении

удваивается с каждой итерацией.

Применение для вычисления квадратного корня

Рассмотрим метод Ньютона на 예제е вычисления квадратного корня.

Если подставить

, то после упрощения выражения получаем:

Первый типичный вариант задачи — когда given дробное number

, и нужно подсчитать его корень с некоторой

точностью

:

double n;

cin >> n;

const double EPS = 1E-15;

double x = 1;

for (;;) {

double nx = (x + n / x) / 2;

if (abs (x - nx) < EPS)  break;

x = nx;

}

printf ("%.15lf", x);

Другой распространённый вариант задачи — когда it is required посчитать 정수 корень (для данного

find наибольшее

такое, что

). Здесь приходится немного изменять 문제 설명 останова 알고리즘а,

поскольку может случиться, что

начнёт "скакать" возле ответа. Поэтому мы добавляем 문제 설명, что если значение

на предыдущем шаге уменьшилось, а на текущем шаге пытается увеличиться, то 알고리즘 надо остановить.

int n;

cin >> n;

int x = 1;

bool decreased = false;

for (;;) {
int nx = (x + n / x) >> 1;
if (x == nx || nx > x && decreased)  break;

decreased = nx < x;

x = nx;

}

cout << x;

Наконец, приведём ещё третий вариант — для случая длинной арифметики. Поскольку number

может быть

достаточно большим, то имеет смысл обратить внимание на начальное приближение. Очевидно, что чем оно ближе к корню, тем быстрее будет достигнут результат. Достаточно простым и эффективным будет брать в качестве

начального приближения number

, где

— количество битов в числе

. Вот код на языке

Java, демонстрирующий этот вариант:

BigInteger n; // 입력ные данные

BigInteger a = BigInteger.ONE.shiftLeft (n.bitLength() / 2);

boolean p_dec = false;

for (;;) {

BigInteger b = n.divide(a).add(a).shiftRight(1);

if (a.compareTo(b) == 0 || a.compareTo(b) < 0 && p_dec)  break;

p_dec = a.compareTo(b) > 0;

a = b;

}

На예제, этот вариант кода выполняется для числа

за

миллисекунд, а если убрать улучшенный

выбор начального приближения (просто начинать с

), то будет выполняться 예제но

миллисекунд.

C# 해법

자동 초안, 제출 전 검토
using System;
using System.Collections.Generic;
using System.Linq;

public static class AlgorithmDraft
{
    // Auto-generated C# draft from the original e-maxx C/C++ listing. Review before production use.
    double n;
    cin >> n;
    const double EPS = 1E-15;
    double x = 1;
    for (;;) {
            double nx = (x + n / x) / 2;
            if (abs (x - nx) < EPS)  break;
            x = nx;
    }
    Console.Write ("%.15lf", x);
    int n;
    cin >> n;
    int x = 1;
    bool decreased = false;
    for (;;) {
            int nx = (x + n / x) >> 1;
            if (x == nx || nx > x && decreased)  break;
            decreased = nx < x;
            x = nx;
    }
    Console.WriteLine( x;
    BigInteger n; // входные данные
    BigInteger a = BigInteger.ONE.shiftLeft (n.bitLength() / 2);
    boolean p_dec = false;
    for (;;) {
            BigInteger b = n.divide(a).add(a).shiftRight(1);
            if (a.compareTo(b) == 0 || a.compareTo(b) < 0 && p_dec)  break;
            p_dec = a.compareTo(b) > 0;
            a = b;
    }
}

C++ 해법

매칭됨/원본
double n;
cin >> n;
const double EPS = 1E-15;
double x = 1;
for (;;) {
        double nx = (x + n / x) / 2;
        if (abs (x - nx) < EPS)  break;
        x = nx;
}
printf ("%.15lf", x);
int n;
cin >> n;
int x = 1;
bool decreased = false;
for (;;) {
        int nx = (x + n / x) >> 1;
        if (x == nx || nx > x && decreased)  break;
        decreased = nx < x;
        x = nx;
}
cout << x;
BigInteger n; // входные данные
BigInteger a = BigInteger.ONE.shiftLeft (n.bitLength() / 2);
boolean p_dec = false;
for (;;) {
        BigInteger b = n.divide(a).add(a).shiftRight(1);
        if (a.compareTo(b) == 0 || a.compareTo(b) < 0 && p_dec)  break;
        p_dec = a.compareTo(b) > 0;
        a = b;
}

Java 해법

자동 초안, 제출 전 검토
import java.util.*;
import java.math.*;

public class AlgorithmDraft {
    // Auto-generated Java draft from the original e-maxx C/C++ listing. Review before production use.
    double n;
    cin >> n;
    const double EPS = 1E-15;
    double x = 1;
    for (;;) {
            double nx = (x + n / x) / 2;
            if (abs (x - nx) < EPS)  break;
            x = nx;
    }
    System.out.print ("%.15lf", x);
    int n;
    cin >> n;
    int x = 1;
    boolean decreased = false;
    for (;;) {
            int nx = (x + n / x) >> 1;
            if (x == nx || nx > x && decreased)  break;
            decreased = nx < x;
            x = nx;
    }
    System.out.println( x;
    BigInteger n; // входные данные
    BigInteger a = BigInteger.ONE.shiftLeft (n.bitLength() / 2);
    booleanean p_dec = false;
    for (;;) {
            BigInteger b = n.divide(a).add(a).shiftRight(1);
            if (a.compareTo(b) == 0 || a.compareTo(b) < 0 && p_dec)  break;
            p_dec = a.compareTo(b) > 0;
            a = b;
    }
}

Материал разбит как 알고리즘ическая 문제: изучить постановку, понять асимптотику и реализовать 알고리즘 на выбранном языке.

Vacancies for this task

활성 채용 with overlapping task tags are 표시됨.

전체 채용
아직 활성 채용이 없습니다.