Pull to refresh

Дихотомия и градиентный спуск

Эта статья посвящена двум алгоритмам поиска — дихотомии и градиентному спуску (как ни странно, на хабре не писали ни про один из них).

Дихотомия

Дихотомия — поиск делением пополам.

Допустим есть отсортированный массив чисел:
a[10] = {0,15,23,37,42,51,69,74,81,99}

Нам надо узнать, каким по счёту в этом массиве находится число 74. Можно, конечно, пройтись по всем элементам массива по порядку и найти нужное число, но тогда, в худшем случае, это займёт O(n) операций. Для небольших значений n это не критично, но при больших значениях n или при большом количестве запросов поиска, это может сильно уменьшить время работы программы. В таком случае можно воспользоваться дихотомией: будем разбивать наш массив на две части и смотреть, в какой из них находится искомое число, далее делаем ту же операцию с найденной частью, и так до тех пор, пока найденная часть не будет состоять из одного элемента.

На примере с нашим массивом:

Разобьём его на две части:
{0,15,23,37,42} и {51,69,74,81,99}. Искомое число 74 находится во второй части. Разобьём её ещё на две:
{51,69,74} и {81,99}. Искомое число находится в первой части. Разобьём её:
{51,69} и {74}. Искомое число находится во второй части. Вторая часть состоит из одного элемента, значит это и есть искомое число.

Теперь реализация этого примера на C++:

int a[10] = {0,15,23,37,42,51,69,74,81,99};
int l = 0;
int r = 9;
while(l<r)
{
 int m = (l+r)/2;
 if(a[m]>=74)
   r = m;
 else
   l = m+1;
}
cout<<l;


Гардиентный спуск

Алгоритм предназначен для нахождения минимума/макчимума функции на заданном отрезке.

К примеру, нужно найти минимум функции y=x*x на интервале [-4,+4]:

image

В данном случае минимум функции y=0 при x=0.

Гардиентный спуск работает следующим образом:

1) Берётся какое-либо значение для шага, не привышающее размер интервала, на котором ищем.
2) Берётся любое значение x, такое, что x+шаг и x-шаг не выходили за пределы интервала.
3) Ищем значение функции для x.
4) Ищем значение функции для x+шаг. Если значении функции f(x+шаг) уменьшилось, по сравнению со значением f(x), то x=x+шаг.
5) Ищем значение функции для x-шаг. Если значении функции f(x-шаг) уменьшилось, по сравнению со значением f(x), то x=x-шаг.
6) Если и f(x+шаг), и f(x-шаг) больше f(x), то уменьшаем шаг в два раза.

Повторяем пункты 3-6 до тех пор, пока шаг>eps, где eps — точность поиска.

Вот как работает алгоритм:

Нашли х и шаг. f(х+шаг) оказалось меньше, чем f(x) и f(x-шаг). Теперь х=х+шаг.

image

Дальше опять повторяется предыдущая ситуация:

image

image

На следующем шаге и f(x+шаг) и f(x-шаг) больше f(x), значит уменьшаем шаг в два раза:

image

image

В конце-концов, мы находим ответ.

Вот реализация алгоритма на C++:

const double eps = 0.001;
double x,step;
x = -3;
step = 1;
while(step>eps)
{
 if(x*x>(x-step)*(x-step))
   x-=step;
 else
  if(x*x>(x+step)*(x+step))
    x+=step;
  else
    step/=2;
}
cout<<x;


Оба алгоритма часто приходится использовать в спортивном программировании, в разных типах задач: поиск максимума/минимума функции(дихотомия и градиентный спуск), поиск в массиве(дихотомия), приближённое решение уравнений(дихотомия и градиентный спуск), часто используются в геометрических задачах(например для поиска точек пересечения отрезка и окружности).
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.