Метод бисекции или метод деления отрезка пополам – простой численный метод для решения нелинейного уравнения вида f(x) = 0.
Описание алгоритма
Метод применим для численного решения уравнения f(x) = 0 для действительной переменной x, где f(x) – непрерывная функция, определенная на интервале [a, b], а f(a) и f(b) имеют противоположные знаки. В этом случае говорят, что a и b заключают в скобки корень, поскольку по теореме о промежуточном значении непрерывная функция f(x) должна иметь хотя бы один корень в интервале (a, b).
На каждом шаге метод делит интервал на две части, вычисляя среднюю точку t = (a + b) / 2 интервала и значение функции f(t) в этой точке. Если только t не является корнем (что очень маловероятно, но возможно), теперь есть только две возможности: либо f(a) и f(t) имеют противоположные знаки и скобки для корня, либо f(t) и f(b) иметь противоположные знаки и заключать в скобки корень. Метод выбирает подинтервал, который гарантированно будет скобкой, в качестве нового интервала, который будет использоваться на следующем шаге. Таким образом, интервал, содержащий ноль f(x), уменьшается по ширине на 50% на каждом шаге. Процесс продолжается до тех пор, пока интервал не станет достаточно малым.
Если f(a) и f(t) имеют противоположные знаки, тогда метод устанавливает t как новое значение для b, а если f(b) и f(t) имеют противоположные знаки, то метод устанавливает t как новое значение а. Если f(t) = 0, то t может быть принято как решение, и процесс останавливается. В обоих случаях новые f(a) и f(b) имеют противоположные знаки, поэтому метод применим к этому меньшему интервалу.
Реализация алгоритма
using System;
class Program
{
/// <summary>
/// Модуль числа
/// </summary>
/// <param name="x">Число</param>
/// <returns>Абсолютное значение числа</returns>
static double Abs(double x)
{
return (x < 0) ? -x : x;
}
/// <summary>
/// Знак числа
/// </summary>
/// <param name="x">Число</param>
/// <returns>-1 если число отрицательное, 1 - если положительное</returns>
static int Sign(double x)
{
return (x < 0) ? -1 : 1;
}
/// <summary>
/// Метод бисекции(деления отрезка пополам)
/// </summary>
/// <param name="function">Функция</param>
/// <param name="a">Начало отрезка</param>
/// <param name="b">Конец отрезка</param>
/// <param name="precision">Точность вычислений</param>
/// <returns></returns>
static double BisectionMethod(Func<double, double> function, double a, double b, double precision = 1e-10)
{
while (true)
{
var t = (a + b) / 2;
if (function(t) == 0.0 || Abs(b - a) < Abs(precision))
{
return t;
}
if (Sign(function(t)) == Sign(function(a)))
{
a = t;
}
else
{
b = t;
}
}
}
static void Main(string[] args)
{
//локальная функция
double f(double x) => 3 * Math.Pow(x, 2) - 6 * x + 1;
Console.WriteLine("Метод бисекции для функции 3x^2 - 6x + 1");
Console.Write("Введите начало отрезка: ");
var a = double.Parse(Console.ReadLine());
Console.Write("Введите конец отрезка: ");
var b = double.Parse(Console.ReadLine());
Console.Write("Введите точность вычислений: ");
var eps = double.Parse(Console.ReadLine());
var result = BisectionMethod(f, a, b, eps);
Console.WriteLine("x = {0}\r\nf({0}) = {1}", result, f(result));
Console.WriteLine("Для выхода нажмите клавишу Enter...");
Console.ReadKey();
}
}
Метод можно применять к любой функции f(x)=0, для этого достаточно изменить локальный метод double f(double x) в коде программы.