Как найти экстремум функции python

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

Nick T's user avatar

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's user avatar

Nick T

25.5k11 gold badges80 silver badges121 bronze badges

answered Apr 13, 2012 at 19:17

ely's user avatar

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

MRocklin's user avatar

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.

Boris Zagoruiko's user avatar

answered Dec 3, 2014 at 9:41

phasor's user avatar

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

zarthur's user avatar

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

rusnasonov's user avatar

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) Пусть

$f(x, y)$ функция, которую необходимо оптимизировать. На первом шаге выбираем три случайные точки (об этом чуть позже) и формируем симплекс (треугольник). Вычисляем значение функции в каждой точке:

$f(V_1)$,

$f(V_2)$,

$f(V_3)$.

Сортируем точки по значениям функции

$f(x, y)$ в этих точках, таким образом получаем двойное неравенство:

$f(V_2) leq f(V_1) leq f(V_3).$

Мы ищем минимум функции, а следовательно, на данном шаге лучшей будет та точка, в которой значение функции минимально. Для удобства переобозначим точки следующим образом:

b =

$V_2$, g =

$V_1$, w =

$V_3$, где best, good, worst — соответственно.

2) На следующем шаге находим середину отрезка, точками которого являются g и b. Т.к. координаты середины отрезка равны полусумме координат его концов, получаем:

$mid = left ( frac {x_1 + x_2} 2; frac {y_1 + y_2} 2 right)$

В более общем виде можно записать так:

$mid = frac 1 nsum_{i=1}^n x_i$

3) Применяем операцию отражения:
Находим точку

$x_r$, следующим образом:

$x_r = mid + α(mid - w)$

Т.е. фактически отражаем точку w относительно mid. В качестве коэффициента берут как правило 1. Проверяем нашу точку: если

$f(x_r) < f(g)$, то это хорошая точка. А теперь попробуем расстояние увеличить в 2 раза, вдруг нам повезет и мы найдем точку еще лучше.

4) Применяем операцию растяжения:
Находим точку

$x_e$ следующим образом:

$x_e = mid + γ(x_r - mid)$

В качестве γ принимаем γ = 2, т.е. расстояние увеличиваем в 2 раза.

Проверяем точку

$x_e$:

Если

$f(x_e) < f(b)$, то нам повезло и мы нашли точку лучше, чем есть на данный момент, если бы этого не произошло, мы бы остановились на точке

$x_r$.

Далее заменяем точку w на

$x_e$, в итоге получаем:

5) Если же нам совсем не повезло и мы не нашли хороших точек, пробуем операцию сжатия.
Как следует из названия операции мы будем уменьшать наш отрезок и искать хорошие точки внутри треугольника.

Пробуем найти хорошую точку

$x_c$:

$x_c = mid + β(w - mid)$

Коэффициент β принимаем равным 0.5, т.е. точка

$x_c$ на середине отрезка wmid.

Существует еще одна операция — shrink (сокращение). В данном случае, мы переопределяем весь симплекс. Оставляем только «лучшую» точку, остальные определяем следующим образом:

$x_j = b + δ(x_j - b)$

Коэффициент δ берут равным 0.5.

По существу передвигаем точки по направлению к текущей «лучшей» точке. Преобразование выглядит следующим образом:

Необходимо отметить, что данная операция дорого обходится, поскольку необходимо заменять точки в симплексе. К счастью было установлено, при проведении большого количества экспериментов, что shrink — трансформация редко случается на практике.

Алгоритм заканчивается, когда:

1) Было выполнено необходимое количество итераций.
2) Площадь симплекса достигла определенной величины.
3) Текущее лучшее решение достигло необходимой точности.

Как и в большинстве эвристических методов, не существует идеального способа выбора инициализирующих точек. Как уже было сказано, можно брать случайные точки, находящиеся недалеко друг от друга для формирования симплекса; но есть решение и получше, которое используется в реализации алгоритма в MATHLAB:

Выбор первой точки

$V_1$ поручаем пользователю, если он имеет некоторое представление о возможном хорошем решении, в противном случае выбирается случайным образом. Остальные точки выбираются исходя из

$V_1$, на небольшом расстоянии вдоль направления каждого измерения:

$V_{i + 1} = V_i + h(V_1, i)*U_i$

где

$U_i$ — единичный вектор.

$h(V_1, i)$ определяется таким образом:

$h(V_1, i)$ = 0.05, если коэффициент при

$U_i$ в определении

$V_1$ не нулевой.

$h(V_1, i)$ = 0.00025, если коэффициент при

$U_i$ в определении нулевой.

Пример:

Найти экстремум следующей функции:

$f(x, y) = x^2 + xy + y^2 - 6x - 9y$

В качестве начальных возьмем точки:

$V_1(0, 0), V_2(1, 0), V_3(0, 1)$

Вычислим значение функции в каждой точке:

$f(V_1) = f(0, 0) = 0$

$f(V_2) = f(1, 0) = -5$

$f(V_3) = f(0, 1) = -8$

Переобозначим точки следующим образом:

$b = V_3(0, 1), g = V_2(1, 0), w = V_1(0, 0)$

Находим середину отрезка bg:

$mid = frac{b+g}2 = left(frac 1 2; frac 1 2 right)$

Находим точку

$x_r$ (операция отражения):

$x_r = mid + α(mid - w),$

если α=1, тогда:

$x_r = 2*mid - w = 2 left(frac 1 2; frac 1 2 right) - left(0, 0 right) = (1, 1)$

Проверяем точку

$x_r$:

$f(x_r) = -12$, т.к.

$f(x_r) < f(b)$ пробуем увеличить отрезок (операция растяжения).

$x_e = mid + γ(x_r - mid), $

если γ = 2, тогда:

$x_e = 2x_r - mid$

$x_e = 2(1, 1) - left(frac 1 2, frac 1 2 right) = (1.5, 1.5)$

Проверяем значение функции в точке

$x_e$:

$f(x_e) = f(1.5, 1.5) = -15.75$

Оказалось, что точка

$x_e $ «лучше» точки b. Следовательно мы получаем новые вершины:

$V_1(1.5, 1.5), V_2(1, 0), V_3(0, 1)$

И алгоритм начинается сначала.

Таблица значений для 10 итераций:

Best Good Worst
$f(0, 1) = -8$ $f(1.0, 0) = -5$ $f(0, 0) = 0$
$f(1.5, 1.5) = -15.75$ $f(0, 1) = -8$ $f(1.0, 0) = -5$
$f(0.25, 3.75) = -20.187$ $f(1.5, 1.5) = -15.75$ $f(0, 1) = -8$
$f(0.25, 3.75) = -20.187$ $f(1.75, 4.25) = -20.1875$ $f(1.5, 1.5) = -15.75$
$f(1.125, 3.375) = -20.671$ $f(1.75, 4.25) = -20.1875$ $f(0.25, 3.75) = -20.1875$
$f(1.140, 3.796) = -20.9638$ $f(1.125, 3.375) = -20.6718$ $f(1.75, 4.25) = -20.1875$
$f(1.140, 3.796) = -20.9638$ $f(1.287, 3.751) = -20.8668$ $f(1.125, 3.375) = -20.6718$
$f(1.140, 3.796) = -20.9638$ $f(1.236, 3.874) = -20.9521$ $f(1.287, 3.751) = -20.8668$
$f(0.990, 4.002) = -20.9951$ $f(1.140, 3.796) = -20.9638$ $f(1.2365, 3.874) = -20.9520$
$f(0.990, 4.002) = -20.9951$ $f(0.895, 3.925) = -20.9855$ $f(1.140, 3.796) = -20.9638$

Аналитически находим экстремум функции, он достигается в точке

$f(1, 4) = -21$.
После 10 итераций мы получаем достаточно точное приближение:

$f(0.990, 4.002) = -20.999916$

Еще о методе:

Алгоритм Нелдера — Мида в основном используется для выбора параметра в машинном обучении. В сущности, симплекс-метод используется для оптимизации параметров модели. Это связано с тем, что данный метод оптимизирует целевую функцию довольно быстро и эффективно (особенно там, где не используется 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 ?

Заранее благодарю.

Прикреплённый файлы:
attachment Снимок.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()

Понравилась статья? Поделить с друзьями:
  • Как составить блок схему примера по информатике
  • Как найти то что искали на компьютере
  • Ассасин 3 как найти гарольда ринга
  • Как исправить ошибку 0x8007000d при обновлении windows 10
  • Как найти комнату в уфе