Метод Ньютона для пошуку коренів
Це ітераційний метод, винайдений Ісааком Ньютоном приблизно у 1664 році. Однак цей метод інколи також називають методом Рафсона, оскільки Рафсон винайшов той самий алгоритм через кілька років після Ньютона, але його статтю опублікували значно раніше.
Задача формулюється так. Маємо таке рівняння:
Ми хочемо розв'язати це рівняння. Точніше, ми хочемо знайти один із його коренів (вважаємо, що корінь існує). Припускаємо, що є неперервною і диференційовною на відрізку .
- Функція неперервна й диференційовна, а похідну можна обчислити (аналітично чи чисельно)?
- Потрібна швидка квадратична збіжність і є достатньо близьке початкове наближення ? (якщо похідна недоступна або поведінка непевна → Бінарний пошук кореня)
- Шукаєте саме корінь рівняння , а не екстремум функції? (якщо потрібен екстремум унімодальної функції → Тернарний пошук)
Алгоритм
Вхідними параметрами алгоритму є не лише функція , а й початкове наближення — деяке , з якого алгоритм починає роботу.

Припустимо, що ми вже обчислили ; обчислимо так. Проведемо дотичну до графіка функції у точці і знайдемо точку перетину цієї дотичної з віссю . Значення покладемо рівним -координаті знайденої точки, і повторюємо весь процес спочатку.
Неважко отримати таку формулу:
Спочатку ми обчислюємо кутовий коефіцієнт — похідну від , а потім визначаємо рівняння дотичної, яке має вигляд:
Дотична перетинає вісь у точці з координатами і :
Тепер, розв'язавши це рівняння, ми отримуємо значення .
Інтуїтивно зрозуміло, що якщо функція «хороша» (гладка), а достатньо близьке до кореня, то буде ще ближчим до шуканого кореня.
Швидкість збіжності є квадратичною, що, умовно кажучи, означає, що кількість точних цифр у наближеному значенні подвоюється з кожною ітерацією.
Застосування для обчислення квадратного кореня
Розгляньмо обчислення квадратного кореня як приклад методу Ньютона.
Якщо підставити , то після спрощення виразу ми отримаємо:
Перший типовий варіант задачі — коли дано раціональне число і потрібно обчислити його корінь з певною точністю eps:
- C++
- Python
- TypeScript
- Go
double sqrt_newton(double n) {
const double eps = 1E-15;
double x = 1;
for (;;) {
double nx = (x + n / x) / 2;
if (abs(x - nx) < eps)
break;
x = nx;
}
return x;
}
def sqrt_newton(n: float) -> float:
eps = 1e-15
x = 1.0
while True:
nx = (x + n / x) / 2
if abs(x - nx) < eps:
break
x = nx
return x
function sqrtNewton(n: number): number {
const eps = 1e-15;
let x = 1;
for (;;) {
const nx = (x + n / x) / 2;
if (Math.abs(x - nx) < eps) break;
x = nx;
}
return x;
}
func sqrtNewton(n float64) float64 {
const eps = 1e-15
x := 1.0
for {
nx := (x + n/x) / 2
if math.Abs(x-nx) < eps {
break
}
x = nx
}
return x
}
Інший поширений варіант задачі — коли нам потрібно обчислити цілочисловий корінь (для заданого знайти найбільше таке, що ). Тут необхідно трохи змінити умову завершення алгоритму, оскільки може статися, що почне «стрибати» біля відповіді. Тому ми додаємо умову: якщо значення зменшилося на попередньому кроці, а на поточному кроці намагається зрости, то алгоритм потрібно зупинити.
- C++
- Python
- TypeScript
- Go
int isqrt_newton(int 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;
}
return x;
}
def isqrt_newton(n: int) -> int:
x = 1
decreased = False
while True:
nx = (x + n // x) >> 1
if x == nx or (nx > x and decreased):
break
decreased = nx < x
x = nx
return x
function isqrtNewton(n: number): number {
let x = 1;
let decreased = false;
for (;;) {
// ділення з округленням донизу для додатних чисел
const nx = Math.floor((x + Math.floor(n / x)) / 2);
if (x === nx || (nx > x && decreased)) break;
decreased = nx < x;
x = nx;
}
return x;
}
func isqrtNewton(n int) int {
x := 1
decreased := false
for {
nx := (x + n/x) >> 1
if x == nx || (nx > x && decreased) {
break
}
decreased = nx < x
x = nx
}
return x
}
Нарешті, розгляньмо третій варіант — для випадку довгої арифметики. Оскільки число може бути доволі великим, має сенс звернути увагу на початкове наближення. Очевидно, що чим воно ближче до кореня, тим швидше буде досягнуто результату. Достатньо просто й ефективно взяти початкове наближення у вигляді числа , де — кількість бітів у числі . Ось код мовою Java, який демонструє цей варіант:
public static BigInteger isqrtNewton(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;
}
return a;
}
Наприклад, цей код виконується за мілісекунд для , а якщо прибрати покращений вибір початкового наближення (просто почавши з ), то він виконуватиметься приблизно за мілісекунд.