/// <summary> /// Рассчитывает оптимальное значение функции. /// </summary> /// <returns>оптимальное значение функции</returns> public double GetOptimal() { // узнаём область поиска var triplet = GetArea(); _logger.Log($"найдена область поиска: {triplet}"); // если область поиска маленькая, // не продолжать искать while (!IsTooSmall(triplet)) { // если количество операций превысило максимальное, выкинуть ошибку if (IsCountDown()) { throw new ApplicationException("не удалось найти оптимального решения"); } // делим область на две части: левую и правую var left = new Triplet { A = triplet.A, B = triplet.Center }; var right = new Triplet { A = triplet.Center, B = triplet.B }; // выбираем лучшую между левой и правой triplet = FindGood(left, right); _logger.Log($"выбрана тройка {triplet}"); } return(triplet.Center); }
/// <summary> /// Рассчитывает область поиска или кидает исключение по превышении количества операций. /// </summary> /// <returns>область поиска оптимума</returns> public Triplet GetArea() { // записываем шаг поиска var step = _step; // создаём тройку с нуля var triplet = new Triplet { A = _start, B = _start + step * 2 }; _logger.Log($"начальная тройка: {triplet}"); // пока не найдём удачную тройку, ищем while (!IsLucky(triplet)) { // если количество операций превысило максимальное, кинуть ошибку if (IsCountDown()) { throw new ApplicationException("не удалось найти область поиска"); } // двигаем провотиположную A сторону на количество шагов triplet.B += step; _logger.Log($"новая тройка поиска: {triplet}"); // удваиваем шаг step *= 2; } ResetCounter(); return(triplet); }
/// <summary> /// Выбирает из двух троек лучшую. /// </summary> /// <remark> /// Учитывает настройку, какой параметр ищем: max / min. /// </remark> /// <param name="left">одна тройка</param> /// <param name="right">другая тройка</param> /// <returns>тройка, которая оказалась лучше</returns> private Triplet FindGood(Triplet left, Triplet right) { // либо оба лучшие, либо оба не лучшие if (!(IsLucky(left) ^ IsLucky(right))) { return (_lookingForMax ? _function(left.Center) > _function(right.Center) ? left : right : _function(left.Center) < _function(right.Center) ? left : right); } else if (IsLucky(left)) { return(left); } else if (IsLucky(right)) { return(right); } else { throw new ApplicationException($"произошла неизвестная ошибка при выборе между {left} и {right}"); } }
/// <summary> /// Узнаёт, не мала ли тройка. /// </summary> /// <param name="triplet">тройка</param> /// <returns>истина, если тройка меньше точности поиска</returns> private bool IsTooSmall(Triplet triplet) => Abs(triplet.B - triplet.A) <= _precision;
/// <summary> /// Удачна ли тройка для этой функции. /// </summary> /// <remark> /// Учитывает настройку, какой параметр ищем: max / min. /// </remark> /// <param name="triplet">тройка</param> /// <returns>истина, если тройка удачная</returns> private bool IsLucky(Triplet triplet) => _lookingForMax ? _function(triplet.Center) >= _function(triplet.A) && _function(triplet.Center) >= _function(triplet.B) : _function(triplet.Center) <= _function(triplet.A) && _function(triplet.Center) <= _function(triplet.B);