How do I find the maximum of a function in Python? I could try to hack together a derivative function and find the zero of that, but is there a method in numpy
(or other library) that can do it for me?
asked Apr 13, 2012 at 19:13
2
You can use scipy.optimize.fmin
on the negative of your function.
def f(x): return -2 * x**2 + 4 * x
max_x = scipy.optimize.fmin(lambda x: -f(x), 0)
# array([ 1.])
Nick T
25.5k11 gold badges80 silver badges121 bronze badges
answered Apr 13, 2012 at 19:17
elyely
74k34 gold badges146 silver badges226 bronze badges
5
If your function is solvable analytically try SymPy. I’ll use EMS’s example above.
In [1]: from sympy import *
In [2]: x = Symbol('x', real=True)
In [3]: f = -2 * x**2 + 4*x
In [4]: fprime = f.diff(x)
In [5]: fprime
Out[5]: -4*x + 4
In [6]: solve(fprime, x) # solve fprime = 0 with respect to x
Out[6]: [1]
Of course, you’ll still need to check that 1 is a maximizer and not a minimizer of f
In [7]: f.diff(x).diff(x) < 0
Out[7]: True
answered Apr 19, 2012 at 13:54
MRocklinMRocklin
55.1k21 gold badges156 silver badges233 bronze badges
I think scipy.optimize.minimize_scalar
and scipy.optimize.minimize
are the preferred ways now, that give you access to the range of techniques, e.g.
solution = scipy.optimize.minimize_scalar(lambda x: -f(x), bounds=[0,1], method='bounded')
for a single variable function that must lie between 0 and 1.
answered Dec 3, 2014 at 9:41
You could try SymPy. SymPy might be able to provide you with the derivative symbolically, find its zeros, and so on.
answered Apr 13, 2012 at 20:50
zarthurzarthur
5215 silver badges10 bronze badges
Maximum of a function with parameters.
import scipy.optimize as opt
def get_function_max(f, *args):
"""
>>> round(get_function_max(lambda x, *a: 3.0-2.0*(x**2)), 2)
3.0
>>> round(get_function_max(lambda x, *a: 3.0-2.0*(x**2)-2.0*x), 2)
3.5
>>> round(get_function_max(lambda x, *a: a[0]-a[1]*(x**2)-a[1]*x, 3.0, 2.0), 2)
3.5
"""
def func(x, *arg):
return -f(x, *arg)
return f(opt.fmin(func, 0, args=args, disp=False)[0], *args)
answered Dec 5, 2014 at 16:16
rusnasonovrusnasonov
7522 gold badges12 silver badges23 bronze badges
Время на прочтение
5 мин
Количество просмотров 56K
Метод Нелдера — Мида — метод оптимизации (поиска минимума) функции от нескольких переменных. Простой и в тоже время эффективный метод, позволяющий оптимизировать функции без использования градиентов. Метод надежен и, как правило, показывает хорошие результаты, хотя и отсутствует теория сходимости. Может использоваться в функции optimize из модуля scipy.optimize популярной библиотеки для языка python, которая используется для математических расчетов.
Алгоритм заключается в формировании симплекса (simplex) и последующего его деформирования в направлении минимума, посредством трех операций:
1) Отражение (reflection);
2) Растяжение (expansion);
3) Сжатие (contract);
Симплекс представляет из себя геометрическую фигуру, являющуюся n — мерным обобщением треугольника. Для одномерного пространства — это отрезок, для двумерного — треугольник. Таким образом n — мерный симплекс имеет n + 1 вершину.
Алгоритм
1) Пусть
функция, которую необходимо оптимизировать. На первом шаге выбираем три случайные точки (об этом чуть позже) и формируем симплекс (треугольник). Вычисляем значение функции в каждой точке:
,
,
.
Сортируем точки по значениям функции
в этих точках, таким образом получаем двойное неравенство:
Мы ищем минимум функции, а следовательно, на данном шаге лучшей будет та точка, в которой значение функции минимально. Для удобства переобозначим точки следующим образом:
b =
, g =
, w =
, где best, good, worst — соответственно.
2) На следующем шаге находим середину отрезка, точками которого являются g и b. Т.к. координаты середины отрезка равны полусумме координат его концов, получаем:
В более общем виде можно записать так:
3) Применяем операцию отражения:
Находим точку
, следующим образом:
Т.е. фактически отражаем точку w относительно mid. В качестве коэффициента берут как правило 1. Проверяем нашу точку: если
, то это хорошая точка. А теперь попробуем расстояние увеличить в 2 раза, вдруг нам повезет и мы найдем точку еще лучше.
4) Применяем операцию растяжения:
Находим точку
следующим образом:
В качестве γ принимаем γ = 2, т.е. расстояние увеличиваем в 2 раза.
Проверяем точку
:
Если
, то нам повезло и мы нашли точку лучше, чем есть на данный момент, если бы этого не произошло, мы бы остановились на точке
.
Далее заменяем точку w на
, в итоге получаем:
5) Если же нам совсем не повезло и мы не нашли хороших точек, пробуем операцию сжатия.
Как следует из названия операции мы будем уменьшать наш отрезок и искать хорошие точки внутри треугольника.
Пробуем найти хорошую точку
:
Коэффициент β принимаем равным 0.5, т.е. точка
на середине отрезка wmid.
Существует еще одна операция — shrink (сокращение). В данном случае, мы переопределяем весь симплекс. Оставляем только «лучшую» точку, остальные определяем следующим образом:
Коэффициент δ берут равным 0.5.
По существу передвигаем точки по направлению к текущей «лучшей» точке. Преобразование выглядит следующим образом:
Необходимо отметить, что данная операция дорого обходится, поскольку необходимо заменять точки в симплексе. К счастью было установлено, при проведении большого количества экспериментов, что shrink — трансформация редко случается на практике.
Алгоритм заканчивается, когда:
1) Было выполнено необходимое количество итераций.
2) Площадь симплекса достигла определенной величины.
3) Текущее лучшее решение достигло необходимой точности.
Как и в большинстве эвристических методов, не существует идеального способа выбора инициализирующих точек. Как уже было сказано, можно брать случайные точки, находящиеся недалеко друг от друга для формирования симплекса; но есть решение и получше, которое используется в реализации алгоритма в MATHLAB:
Выбор первой точки
поручаем пользователю, если он имеет некоторое представление о возможном хорошем решении, в противном случае выбирается случайным образом. Остальные точки выбираются исходя из
, на небольшом расстоянии вдоль направления каждого измерения:
где
— единичный вектор.
определяется таким образом:
= 0.05, если коэффициент при
в определении
не нулевой.
= 0.00025, если коэффициент при
в определении нулевой.
Пример:
Найти экстремум следующей функции:
В качестве начальных возьмем точки:
Вычислим значение функции в каждой точке:
Переобозначим точки следующим образом:
Находим середину отрезка bg:
Находим точку
(операция отражения):
если α=1, тогда:
Проверяем точку
:
, т.к.
пробуем увеличить отрезок (операция растяжения).
если γ = 2, тогда:
Проверяем значение функции в точке
:
Оказалось, что точка
«лучше» точки b. Следовательно мы получаем новые вершины:
И алгоритм начинается сначала.
Таблица значений для 10 итераций:
Best | Good | Worst |
---|---|---|
Аналитически находим экстремум функции, он достигается в точке
.
После 10 итераций мы получаем достаточно точное приближение:
Еще о методе:
Алгоритм Нелдера — Мида в основном используется для выбора параметра в машинном обучении. В сущности, симплекс-метод используется для оптимизации параметров модели. Это связано с тем, что данный метод оптимизирует целевую функцию довольно быстро и эффективно (особенно там, где не используется shrink — модификация).
С другой стороны, в силу отсутствия теории сходимости, на практике метод может приводить к неверному ответу даже на гладких (непрерывно дифференцируемых) функциях. Также возможна ситуация, когда рабочий симплекс находится далеко от оптимальной точки, а алгоритм производит большое число итераций, при этом мало изменяя значения функции. Эвристический метод решения этой проблемы заключается в запуске алгоритма несколько раз и ограничении числа итераций.
Реализация на языке программирования python:
Создаем вспомогательный класс Vector и перегружаем операторы для возможности производить с векторами базовые операции. Я намерено не использовал вспомогательные библиотеки для реализации алгоритма, т.к. в таком случае зачастую снижается восприятие.
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Vector(object):
def __init__(self, x, y):
""" Create a vector, example: v = Vector(1,2) """
self.x = x
self.y = y
def __repr__(self):
return "({0}, {1})".format(self.x, self.y)
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
def __sub__(self, other):
x = self.x - other.x
y = self.y - other.y
return Vector(x, y)
def __rmul__(self, other):
x = self.x * other
y = self.y * other
return Vector(x, y)
def __truediv__(self, other):
x = self.x / other
y = self.y / other
return Vector(x, y)
def c(self):
return (self.x, self.y)
# objective function
def f(point):
x, y = point
return x**2 + x*y + y**2 - 6*x - 9*y
def nelder_mead(alpha=1, beta=0.5, gamma=2, maxiter=10):
# initialization
v1 = Vector(0, 0)
v2 = Vector(1.0, 0)
v3 = Vector(0, 1)
for i in range(maxiter):
adict = {v1:f(v1.c()), v2:f(v2.c()), v3:f(v3.c())}
points = sorted(adict.items(), key=lambda x: x[1])
b = points[0][0]
g = points[1][0]
w = points[2][0]
mid = (g + b)/2
# reflection
xr = mid + alpha * (mid - w)
if f(xr.c()) < f(g.c()):
w = xr
else:
if f(xr.c()) < f(w.c()):
w = xr
c = (w + mid)/2
if f(c.c()) < f(w.c()):
w = c
if f(xr.c()) < f(b.c()):
# expansion
xe = mid + gamma * (xr - mid)
if f(xe.c()) < f(xr.c()):
w = xe
else:
w = xr
if f(xr.c()) > f(g.c()):
# contraction
xc = mid + beta * (w - mid)
if f(xc.c()) < f(w.c()):
w = xc
# update points
v1 = w
v2 = g
v3 = b
return b
print("Result of Nelder-Mead algorithm: ")
xk = nelder_mead()
print("Best poits is: %s"%(xk))
Спасибо за чтение статьи. Надеюсь она была Вам полезна и Вы узнали много нового.
С вами был FUNNYDMAN. Удачной оптимизации!)
The bounds argument goes [(lower1,upper1),(lower2,upper2)], not [(lower1,lower2),(upper1,upper2)]. If you look at your result (max_x) you will see «ERROR: NO FEASIBLE SOLUTION», which I am guessing is because your bounds specify an empty set.
Here is a correct way to call the function. I assume the square root is just an example. I used -x**2 instead.
import scipy.optimize as opt
import scipy
from numpy import *
def f(x):
print x
return -x**(2)
max_x = opt.fmin_l_bfgs_b(lambda x: -f(x), 1.0, bounds=[(-9,9)],approx_grad=True)
Because you are not specifying a gradient function, you need to set approx_grad=True. The 1.0 is my initial guess for the maximum (although it is obviously zero for this example). I added a print statement so I can see each time the function is called, but that’s normally not necessary. For more details on different ways to call fmin_l_bfgs_b, see here.
The above code results in:
[ 1.]
[ 1.]
[ 1.00000001]
[-0.99999999]
[-0.99999999]
[-0.99999998]
[ 0.001]
[ 0.001]
[ 0.00100001]
[ -5.01108742e-09]
[ -5.01108742e-09]
[ 4.98891258e-09]
And max_x looks like this:
(array([ -5.01108742e-09]),
array([ 2.51109971e-17]),
{'funcalls': 4,
'grad': array([ -2.21748344e-11]),
'task': 'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL',
'warnflag': 0})
Уведомления
- Начало
- » Центр помощи
- » экстремум функции
#1 Окт. 17, 2021 14:43:34
экстремум функции
Добрый день.
Задался целью изучить Python.
Поставил перед собой задачу — портировать расчет, который используем в работе, из маткада в python.
И вроде все шло удачно, но столкнулся с таким моментом, который не получается реализовать, а именно найти экстремум функции Y(Х) в заданных интервалах X, с заданием начального приближения переменной Х. Вот аналогичный пример в маткаде ссылка на пример — интересующий фрагмент прикрепил картинкой.
Все, что смог накопать, это использование библиотеки sympy, а также использование функции maximum и minimum из sympy.calculus.util:
x = symbols(“x”)
f = -1*(x ** 7) + 5 * (x **3) — 3 * x
interv = Interval(-2.0, 0.0)
res_min = minimum(f, x, interv)
res_max = maximum(f, x, interv)
Но как задать начальное приближение переменной Х = 1, чтобы результаты получились как в примере -0,452 и -1,162 ?
Заранее благодарю.
Прикреплённый файлы:
Снимок.PNG (122,0 KБ)
Офлайн
- Пожаловаться
#2 Окт. 17, 2021 16:32:19
экстремум функции
x800
Задался целью изучить Python.
Поставил перед собой задачу — портировать расчет, который используем в работе, из маткада в python.
Зачем? Вот ты теперь сидишь и сделать ничего не можешь с этим. Обучение классное, конечно. Это как мальчишка решил научиться плавать и сиганул на десятиметровую глубину, а там понял, что плавать-то он не умеет, и орёт окружающим, чтобы спасли его, бросили ему спасательный круг.
Хочешь научиться — начинай, как все начинают, с простого чего-нибудь. Если плавать учишься, начинай с лужи, где воды по пояс. А если ты такой умный и тонешь уже в какой-то яме, то тони тихонько, не ори.
Отредактировано py.user.next (Окт. 17, 2021 16:36:47)
Офлайн
- Пожаловаться
#3 Окт. 17, 2021 18:55:58
экстремум функции
from scipy import optimize def f(x): return -1*(x ** 7) + 5 * (x **3) - 3 * x print(optimize.minimize(f,[1]))
fun: -0.8981283963885099 hess_inv: array([[0.07846038]]) jac: array([-1.82539225e-06]) message: 'Optimization terminated successfully.' nfev: 14 nit: 5 njev: 7 status: 0 success: True x: array([0.45161859]) Process finished with exit code 0
Вы чего-нибудь понимаете?…
я нет…
при x = -1
fun: -1.49841576523223 hess_inv: array([[0.01842377]]) jac: array([5.96046448e-08]) message: 'Optimization terminated successfully.' nfev: 16 nit: 6 njev: 8 status: 0 success: True x: array([-1.16240037]) Process finished with exit code 0
Отредактировано xam1816 (Окт. 17, 2021 19:04:46)
Офлайн
- Пожаловаться
#4 Окт. 17, 2021 19:12:52
экстремум функции
xam1816
x: array(0.45161859)
xam1816
x: array(-1.16240037)
то, что нужно!
спасибо большое!
почитал про optimize.minimize, получается и интервал (как в примере, -2 < x < 0 ) задать можно через параметр bounds!
x1=[(-2, 0)] print(optimize.minimize(f,[1], bounds = x1))
то, что искал! спасибо, xam1816, еще раз.
Отредактировано x800 (Окт. 17, 2021 21:25:21)
Офлайн
- Пожаловаться
- Начало
- » Центр помощи
- » экстремум функции
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
import numpy as np import matplotlib.pyplot as plt from math import sqrt,sin,cos #Задаем функцию def f(x): return sin(x) #Основа метода def method(x1,x2,h,eps): #Проверка на содержание корня if f(x1)*f(x2)>0: return None,None else: #Проверка на содержание корня в одной из заданных точек if f(x1) == 0 or f(x2) == 0: if f(x1) == 0: xk=x1 intt=0 return xk,intt if f(x2) == 0: xk=x2 intt=0 return xk,intt else: #Вычисление кол-ва иттераций и поиск корня xk=0 intt=0 while abs(x1-x2)>eps: x3=x1+((3-sqrt(5))/2)*(x2-x1) x4=x1+((sqrt(5)-1)/2)*(x2-x1) if abs(f(x3))>abs(f(x4)): x1=x3 else: x2=x4 intt+=1 xk=x3 return xk,intt #Функция для построения таблицы def table(k,x1,x2,xk,intt,Err): print('-------------------------------------------------------- --------------') print('|{0:^7}|{1:^8.4f}|{2:^8.4f}|{3:^8.4f}|{4:^11.4e}|{5:^11} |{6:^9}|'.format(k,x1,x2,xk,f(xk),intt,Err)) #Основная функция программы def main(): #Ввод значений a=float(input('Введите начало отрезка: ')) b=float(input('Введите конец отрезка: ')) h=float(input('Введите шаг разбиений: ')) eps=float(input('Введите точность: ')) mint=int(input('Введите максимальную итерацию: ')) print(' Метод золотого сечения') print('-------------------------------------------------------- --------------') print('| № | x1 | x2 | xk | f(xk) | itter | Err |') #Задаем начальные значения x1 и x2 и списки для построения графика x1=a x2=a+h xmas=[] fmas=[] k=1 #Ставим флаг для того, чтобы убрать ошибку при выводе наложения корней flag=0 #Ставим флаг для проверки наличия корней flag1=0 while x2<=b: if f(x2)==0: flag=1 Err=0 xk,intt=method(x1,x2,h,eps) if xk != None: #вывод ошибки if intt >= mint: Err=1 #добавление корней в список и таблицу и проверка на наличие корней if not (f(x1)==0 and flag==1) : table(k,x1,x2,xk,intt,Err) k+=1 xmas.append(xk) fmas.append(f(xk)) flag1=1 x1=x2 x2=x1+h if x2>b and x1 != b: x2=b if flag1==0: print('| НЕТ КОРНЕЙ |') print('------------------------------------------------------ ----------------') print('Тип ошибок: ') print('0 - ошибок нет ') print('1 - превышено число итераций ') print('Нажмите enter, чтобы построить график') input() makeGraph(a,b,xmas,fmas) #Постройка графика def makeGraph(a,b,xMas,fMas): ls = np.linspace(a,b,num = round(b - a) * 100) plt.plot(ls,fLinespace(ls),xMas,fMas,'ro') plt.grid(True) plt.show() def fLinespace(ln): mas = [] for i in ln: mas.append(f(i)) return mas main() |