- •Одномерная оптимизация
- •Метод дихотомии
- •Метод золотого сечения
- •Метод Ньютона
- •Многомерная оптимизация по направлению
- •Метод дихотомии
- •Метод золотого сечения
- •Метод Ньютона
- •Листинг кода метода дихотомии
- •Тест метода дихотомии
- •Листинг кода метода золотого сечения
- •Тест метода золотого сечения
- •Листинг кода метода Ньютона
- •Тест метода Ньютона
BТест метода дихотомии
1using NUnit.Framework;
2using System;
3
4namespace MathUtilities.Tests
5{
6
7
8
9
10
11
12
13
14
15
16
17
[TestFixture]
[TestOf(typeof(DichotomyOptimizer))] public class DichotomyOptimizerTest
{
[Test]
public void TestLeftParabola()
{
var o = new DichotomyOptimizer(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 DichotomyOptimizer(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 DichotomyOptimizer(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(Triplet))] public class TripletTest
16
46{
47[Test]
48[TestCase(1, 3, 2)]
49[TestCase(1, -1, 0)]
50[TestCase(-5, -3, -4)]
51public void TestCenter(double left, double right, double expected)
52{
53 |
var t = |
new Triplet{A = left, B = right}; |
54 |
var got |
= t.Center; |
55 |
Assert.AreEqual(expected, got); |
56}
57}
58}
17
CЛистинг кода метода золотого сечения
1using static System.Math;
2using System;
3
4namespace MathUtilities
5{
6/// <summary>
7/// Находит оптимум функции одной переменной при помощи метода золотого
,! сечения.
8/// </summary>
9public class GoldenOptimizer
10{
11/// <summary>
12/// Функция для исследования.
13/// </summary>
14private readonly Func<double, double> _function;
15
16
17
18
19
20
///<summary>
///Начало поиска.
///</summary>
private readonly double _start;
21
22
23
24
25
///<summary>
///Шаг поиска.
///</summary>
private readonly double _step;
26/// <summary>
27/// Точность поиска. Влияет на размер области поиска.
28/// </summary>
29private readonly double _precision;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
///<summary>
///Максимальное количество операций.
///</summary>
private readonly uint _operationAmount;
///<summary>
///Смотрим ли максимум или минимум.
///</summary>
private readonly bool _lookingForMax;
///<summary>
///Счётчик операций.
///</summary>
private uint counter;
18
45
46/// <summary>
47/// Стандартный конструктор анализатора.
48/// </summary>
49/// <param name="function">функция для исследования</param>
50/// <param name="start">точка начала поиска</param>
51/// <param name="step">шаг поиска</param>
52/// <param name="precision">точность поиска</param>
53/// <param name="operationsAmount">максимальное количество
,! операций</param>
54/// <param name="lookingForMax">смотрим ли максимум или минимум</param>
55public GoldenOptimizer(
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Func<double, double> function, double start = 0,
double step = 1E-02, double precision = 1E-04,
uint operationsAmount = 150, bool lookingForMax = false
) {
_function = function; _start = start;
//разворачивает шаг в нужную сторону
//учитывает max / min
_step = function(start + step) > function(start - step) ? lookingForMax ? step : -step
: lookingForMax ? -step : step;
_precision = precision; _operationAmount = operationsAmount; _lookingForMax = lookingForMax;
ResetCounter();
}
78/// <summary>
79/// Рассчитывает оптимальное значение функции.
80/// </summary>
81/// <returns>оптимальное значение функции</returns>
82public double GetOptimal()
83{
84
85
86
87
88
89
// узнаём область поиска var triplet = GetArea();
//если область поиска маленькая,
//не продолжать искать
while (!IsTooSmall(triplet))
{
19
90 |
// если количество операций превысило максимальное, выкинуть |
|
,! ошибку |
91 |
if (IsCountDown()) |
92 |
throw new ApplicationException("не удалось найти оптимального |
|
,! решения"); |
93 |
|
94
95
96
// выбираем лучшую между левой и правой triplet = FindGood(triplet);
}
97 |
|
98 |
// всё равно передаём центральную точку |
99 |
return triplet.A + (triplet.B - triplet.A) / 2; |
100 |
} |
101 |
|
102/// <summary>
103/// Рассчитывает область поиска или кидает исключение по превышении ,! количества операций.
104/// </summary>
105/// <returns>область поиска оптимума</returns>
106public GoldenTriplet GetArea()
107{
108 |
// записываем шаг |
поиска |
109 |
var step = _step; |
|
110 |
// создаём тройку |
с нуля |
111 |
var triplet = new GoldenTriplet {A = _start, RightCenter = _start + |
|
|
,! step}; |
|
112 |
// пока не найдём удачную тройку, ищем |
|
113 |
while (!IsLucky(triplet)) |
|
114 |
{ |
|
115 |
// если количество операций превысило максимальное, кинуть |
|
|
,! ошибку |
|
116
117
if (IsCountDown())
throw new ApplicationException("не удалось найти область
,! поиска");
118
119
120
121
122
// двигаем провотиположную A сторону на количество шагов triplet.LeftCenter = triplet.B;
}
123
124
125
126
127
128
129
130
ResetCounter(); return triplet;
}
///<summary>
///Удачна ли тройка для этой функции.
///</summary>
///<remark>
20
131/// Учитывает настройку, какой параметр ищем: max / min.
132/// </remark>
133/// <param name="triplet">тройка</param>
134/// <returns>истина, если тройка удачная</returns>
135private bool IsLucky(GoldenTriplet triplet) =>
136
137
138
139
140
141
_lookingForMax
? _function(triplet.RightCenter) >= _function(triplet.A) && _function(triplet.RightCenter) >= _function(triplet.B)
: _function(triplet.RightCenter) <= _function(triplet.A) && _function(triplet.RightCenter) <= _function(triplet.B);
142/// <summary>
143/// Узнаёт, не мала ли тройка.
144/// </summary>
145/// <param name="triplet">тройка</param>
146/// <returns>истина, если тройка меньше точности поиска</returns>
147private bool IsTooSmall(GoldenTriplet triplet) =>
148 |
Abs(triplet.B - triplet.A) <= _precision; |
149
150
151/// <summary>
152/// Выбирает из двух частей тройки лучшую.
153/// </summary>
154/// <remark>
155/// Учитывает настройку, какой параметр ищем: max / min.
156/// </remark>
157/// <param name="old">тройка для рассмотрения</param>
158/// <returns>новая тройка, оказавшаяся лучше</returns>
159private GoldenTriplet FindGood(GoldenTriplet old)
160{
161 |
if (_lookingForMax) |
162 |
return _function(old.LeftCenter) > _function(old.RightCenter) ? |
|
,! new GoldenTriplet {A = old.LeftCenter, B = old.B} : new |
|
,! GoldenTriplet {A = old.A, B = old.RightCenter}; |
163 |
else |
164 |
return _function(old.LeftCenter) < _function(old.RightCenter) ? |
|
,! new GoldenTriplet {A = old.A, B = old.RightCenter} : new |
|
,! GoldenTriplet {A = old.LeftCenter, B = old.B}; |
165 |
} |
166 |
|
167/// <summary>
168/// Увеличивает счётчик на 1 и передаёт, не превысило ли количество ,! операций максимальное.
169/// </summary>
170/// <returns>истина, если количество операций превысило
,! максимальное</returns>
171 private bool IsCountDown() =>
21
172 |
counter++ > _operationAmount; |
173 |
|
174 |
/// <summary> |
175 |
/// Сбрасывает счётчик. |
176 |
/// </summary> |
177 |
private void ResetCounter() => |
178 |
counter = 0; |
179 |
} |
180
181/// <summary>
182/// Тройка с золотым сечением.
183/// </summary>
184public class GoldenTriplet
185{
186/// <summary>
187/// Золотое число. 1 - (\sqrt(5) - 1) / 2
188/// </summary>
189public const double SmallGolden = 0.3819660113;
190
191/// <summary>
192/// Золотое число. (\sqrt(5) - 1) / 2
193/// </summary>
194public const double Golden = 0.6180339887;
195
196/// <summary>
197/// Золотое число. (\sqrt(5) + 1) / 2
198/// </summary>
199public const double BigGolden = 1.6180339887;
200
201
202
203
204
205
///<summary>
///Левая часть тройки.
///</summary>
public double A { get; set; }
206
207
208
209
210
///<summary>
///Правая часть тройки.
///</summary>
public double B { get; set; }
211/// <summary>
212/// Центральная часть тройки ближе к правому краю.
213///
214/// Сеттер на самом деле устанавливает правую часть.
215/// </summary>
216/// <remark>
217/// Золотое сечение.
218/// </remark>
22
219public double RightCenter { get => A + (B - A) * Golden; set => B = value ,! * BigGolden - A * Golden; }
220
221/// <summary>
222/// Центральная часть тройки ближе к левому краю.
223/// </summary>
224/// <remark>
225/// Золотое сечение.
226/// </remark>
227public double LeftCenter { get => B - (B - A) * Golden; set => B = value
,! + BigGolden * (value - A); }
228 |
|
229 |
public override string ToString() => |
230 |
$"{A} - {LeftCenter} - {RightCenter} - {B}"; |
231}
232}
23