- •Одномерная оптимизация
- •Метод дихотомии
- •Метод золотого сечения
- •Метод Ньютона
- •Многомерная оптимизация по направлению
- •Метод дихотомии
- •Метод золотого сечения
- •Метод Ньютона
- •Листинг кода метода дихотомии
- •Тест метода дихотомии
- •Листинг кода метода золотого сечения
- •Тест метода золотого сечения
- •Листинг кода метода Ньютона
- •Тест метода Ньютона
DТест метода золотого сечения
1using System;
2using NUnit.Framework;
3
4namespace MathUtilities.Tests
5{
6
7
8
9
10
11
12
13
14
15
16
17
[TestFixture]
[TestOf(typeof(GoldenOptimizer))] public class GoldenOptimizerTest
{
[Test]
public void TestLeftParabola()
{
var o = new GoldenOptimizer(LeftParabola); var got = o.GetOptimal(); Assert.AreEqual(-1, got, 1E-04);
}
18
19
20
21
22
23
24
25
[Test]
public void TestParabola()
{
var o = new GoldenOptimizer(Parabola, start: 1); var got = o.GetOptimal();
Assert.AreEqual(0, got, 1E-04);
}
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[Test]
public void TestAreaNotFound()
{
var o = new GoldenOptimizer(Line); Assert.Catch<ApplicationException>(() => o.GetOptimal());
}
private double LeftParabola(double x) => x * x + 2 * x + 1;
private double Parabola(double x) => x * x;
private double Line(double x) => 2 * x;
}
[TestFixture]
[TestOf(typeof(GoldenTriplet))] public class GoldenTripletTest
{
24
46[Test]
47[TestCase(0, 1, 0.61803)]
48[TestCase(0, 10, 6.1803)]
49[TestCase(0, -10, -6.1803)]
50public void TestRightCenter(double left, double right, double expected)
51{
52 |
var t = new GoldenTriplet{A = left, B = right}; |
53 |
var got = t.RightCenter; |
54 |
Assert.AreEqual(expected, got, 1E-03); |
55 |
} |
56
57[Test]
58[TestCase(0, 1, 0.3819)]
59[TestCase(0, 10, 3.8196)]
60[TestCase(0, -10, -3.8196)]
61public void TestLeftCenter(double left, double right, double expected)
62{
63 |
var t = new GoldenTriplet{A = left, B = right}; |
64 |
var got = t.LeftCenter; |
65 |
Assert.AreEqual(expected, got, 1E-03); |
66 |
} |
67
68[Test]
69[TestCase(0, 0.61803, 1)]
70[TestCase(0, 6.1803, 10)]
71[TestCase(0, -6.1803, -10)]
72public void TestRightFromRightCenter(double left, double rightCenter, ,! double expected)
73{
74 |
var t = new GoldenTriplet{A = left, RightCenter = rightCenter}; |
75 |
var got = t.B; |
76 |
Assert.AreEqual(expected, got, 1E-03); |
77 |
} |
78
79[Test]
80[TestCase(0, 0.3819, 1)]
81[TestCase(0, 3.8196, 10)]
82[TestCase(0, -3.8196, -10)]
83public void TestRightFromLeftCenter(double left, double leftCenter, ,! double expected)
84{
85 |
var t = |
new GoldenTriplet{A = left, LeftCenter = leftCenter}; |
86 |
var got |
= t.B; |
87 |
Assert.AreEqual(expected, got, 1E-03); |
88}
89}
90}
25
EЛистинг кода метода Ньютона
1using static System.Math;
2using System;
3
4namespace MathUtilities
5{
6
7
8
9
///<summary>
///Класс для одномерной оптимизации методом Ньютона.
///</summary>
public class NewtonOptimizer
10{
11/// <summary>
12/// Функция для исследования.
13/// </summary>
14private readonly Func<double, double> _function;
15
16/// <summary>
17/// Класс для нахождения производной, градиента и Гессиана.
18/// </summary>
19private readonly Derivative _derivative;
20
21
22
23
24
25
///<summary>
///Начальный вектор поиска.
///</summary>
private readonly double _start;
26
27
28
29
30
///<summary>
///Точность поиска.
///</summary>
private readonly double _precision;
31
32
33
34
35
///<summary>
///Максимальное количество итераций.
///</summary>
private readonly int _operationsAmount;
36
37
38
39
40
///<summary>
///Счётчик операций.
///</summary>
private int counter;
41/// <summary>
42/// Стандартный конструктор
43/// </summary>
44/// <param name="function">функция для исследования</param>
45/// <param name="start">начальный вектор</param>
26
46/// <param name="precision">точность поиска</param>
47/// <param name="operationsAmount">максимальное количество ,! итераций</param>
48public NewtonOptimizer(
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Func<double, double> function, double start = 0,
double precision = 1E-08, int operationsAmount = 15
) {
_function = function;
_derivative = new Derivative(function); _start = start;
_precision = precision; _operationsAmount = operationsAmount;
ResetCounter();
}
63/// <summary>
64/// Рассчитывает точку, в которой функция имеет минимум.
65/// </summary>
66public double GetOptimal()
67{
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// считаем производную
var derivative = _derivative.GetDerivative(_start);
// считаем вторую производную
var secDerivative = _derivative.GetSecondDerivative(_start);
// считаем текущий вектор по формуле
var current = _start - derivative / secDerivative;
// пока разница между значениями не станет меньше заданного while (!Math.Equals(derivative, 0))
{
//если операций не больше максимального if (IsCountDown())
throw new ApplicationException("не нашлось решения");
//рассчитать производную в текущей точке
derivative = _derivative.GetDerivative(current);
// считаем вторую производную
secDerivative = _derivative.GetSecondDerivative(current);
// считаем текущий вектор по формуле
current = current - derivative / secDerivative;
}
89 |
// вернуть найденный вектор |
90 |
return current; |
91 |
} |
27
92
93/// <summary>
94/// Не превысило ли количество операций максимальное.
95/// </summary>
96private bool IsCountDown() =>
97 |
counter++ > _operationsAmount; |
98 |
|
99
100
101
102
103
104
///<summary>
///Обнуляет количество проделанных операций.
///</summary>
private void ResetCounter() => counter = 0;
105/// <summary>
106/// Проверяет, является ли новое решение слишком близким.
107/// </summary>
108/// <param name="previous">старое решение</param>
109/// <param name="current">текущее решение</param>
110/// <returns>истина, если решения находятся близко друг к ,! другу</returns>
111private bool IsTooSmall(double previous, double current) =>
112 |
Abs(_function(current) - _function(previous)) < _precision; |
113 |
} |
114
115/// <summary>
116/// Класс для численного дифференцирования.
117/// </summary>
118public class Derivative
119{
120/// <summary>
121/// Функция для дифференцирования.
122/// </summary>
123private readonly Func<double, double> _function;
124
125/// <summary>
126/// Стандартный конструктор для дифференцирования.
127/// </summary>
128/// <param name="function">функция для дифференцирования</param>
129public Derivative(Func<double, double> function) =>
130
131
_function = function;
132/// <summary>
133/// Рассчитывает обычную производную по одному аргументу.
134/// </summary>
135/// <param name="point">точка, в которой рассчитывается
,! производная</param>
136 /// <returns>значение производной в точке</returns>
28
137
138
139
140
141
142
143
144
public double GetDerivative(double point)
{
var eps = Math.MinimalTenPower(point); var left = _function(point + eps);
var right = _function(point - eps); return (left - right) / (2 * eps);
}
145/// <summary>
146/// Рассчитывает обычную вторую производную по одному аргументу.
147/// </summary>
148/// <param name="point">точка, в которой рассчитывается
,! производная</param>
149/// <returns>значение второй производной в точке</returns>
150public double GetSecondDerivative(double point)
151{
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
var eps = Math.MinimalTenPower(point); var left = _function(point + 2 * eps); var right = _function(point - 2 * eps);
return (left + right - 2 * _function(point)) / (4 * eps * eps);
}
}
///<summary>
///Отдельные специальные вычисления.
///</summary>
public static class Math
{
///<summary>
///Количество значащих цифр для сравнения.
///</summary>
public const int TYPICAL_POWER = 5;
169
170
171
172
173
174
175
176
177
178
179
180
181
182
///<summary>
///Примерно сравнивает числа.
///</summary>
///<param name="a">одно число</param>
///<param name="b">другое число</param>
///<returns>истина, если числа равны хотя бы
///до -5 знака после значащей цифры</returns>
public static bool Equals(double a, double b)
{
var minimalTenPower = Max(MinimalTenPower(a), MinimalTenPower(b)); if (Abs(a - b) < minimalTenPower)
return true;
return false;
29
183 |
} |
184
185/// <summary>
186/// Передаёт минимальное количество значащих цифр от 0 до 15.
187/// </summary>
188/// <remark>
189/// Не работает для чисел \in (1; 10).
190/// </remark>
191/// <param name="n">число</param>
192/// <param name="restrict15">использовать ограничение на 15 цифр; ,! по-умолчанию - истина</param>
193/// <returns>минимальное количество значащих цифр</returns>
194public static int MinimalPower(double n, bool restrict15 = true)
195{
196
197
if (n == 0) return TYPICAL_POWER;
198 |
// модулируем |
199 |
var absolute = Abs(n); |
200 |
// находим оригинальную степень |
201 |
var power = Log10(absolute); |
202 |
// убираем знаки после запятой |
203 |
var rounded = Round(power); |
204 |
// новая степень десятки - количество значащих цифр; |
205 |
// удаляем минус из количества цифр |
206 |
var answer = (int) Abs(rounded - TYPICAL_POWER); |
207 |
// избавляемся от чисел, меньше 1E-15 или больше 1E+15 |
208 |
answer = restrict15 && answer > 15 ? 15 : answer; |
209 |
|
210 |
return answer; |
211 |
} |
212 |
|
213/// <summary>
214/// Передаёт минимальную степень десятки, соответствующую минимальному ,! количеству значащих цифр.
215/// </summary>
216/// <remark>
217/// Не работает для чисел \in (1; 10).
218/// </remark>
219/// <param name="n">число</param>
220/// <returns>минимальная степень десятки, соответствующая минимальному ,! количеству значащих цифр</returns>
221public static double MinimalTenPower(double n) =>
222 |
Pow(10, -1 * MinimalPower(n)); |
223}
224}
30