# -*- coding: utf-8 -*-
# author: 'boliang'
# date: 2018/4/15 21:49


import math
import matplotlib.pyplot as plt
import numpy as np

class Solution_golds(object):
   def __init__(self, fun=None, a=0, b=0, epsilon=1e-5, deta=1e-4):
       if fun == None:
           return
       self.reset(fun, a, b, epsilon, deta)

   def reset(self, fun, a=0, b=0, epsilon=1e-5, deta=1e-4):
       self.__fun_str = fun
       self.__fun = lambda x: eval(fun)
       self.__t = (math.sqrt(5) - 1) / 2
       if a == 0 and b == 0:
           a, b = self.__get_section(0, 0.5)
       self.__p = a + (1 - self.__t) * (b - a)
       self.__q = a + self.__t * (b - a)
       self.__epsilon = epsilon
       self.__deta = deta
       self.__a = a
       self.__b = b
       self.__pCal = self.__fun(self.__p)
       self.__qCal = self.__fun(self.__q)

   # 进退法, 获得极小值点的搜索区间
   def __get_section(self, alpha, h):
       ak = alpha
       k = 0
       cal_k = self.__fun(ak)
       while True:
           akp = ak + h
           cal_kp = self.__fun(akp)
           if cal_kp < cal_k:
               h = 2 * h
               alpha = ak
               ak = akp
               cal_k = cal_kp
               k += 1
           else:
               if k == 0:
                   # 反向搜索
                   h = -h
                   alpha = akp
                   akp = ak
                   cal_kp = cal_k
                   k = 1
               else:
                   break

       return [min(alpha, akp), max(alpha, akp)]

   def __draw_image(self, min_x, min_y):
       X = np.arange(self.__a-5, self.__b+5, 0.01)
       Y = list(map(lambda x: self.__fun(x), X))
       plt.plot(X, Y)
       plt.title(self.__fun_str, fontSize=16)
       plt.xlabel('X', fontSize=16)
       plt.ylabel('Y', fontSize=16)
       plt.text(min_x, min_y, 'X', color='red', fontSize=14)
       plt.text(min_x+0.382, min_y-0.618, '({:.4f}, {:.4f})'.format(min_x, min_y), fontSize=14)
       plt.show()

   def run(self):
       step = 1
       while abs(self.__b - self.__a) > self.__deta:
           if self.__pCal <= self.__qCal:
               if abs(self.__q - self.__a) <= self.__epsilon:
                   break
               else:
                   self.__b = self.__q
                   self.__qCal = self.__pCal
                   self.__q = self.__p
                   self.__p = self.__a + (1-self.__t)*(self.__b - self.__a)
                   self.__pCal = self.__fun(self.__p)
           else:
               if abs(self.__b - self.__p) <= self.__epsilon:
                   break
               else:
                   self.__a = self.__p
                   self.__pCal = self.__qCal
                   self.__p = self.__q
                   self.__q = self.__a + self.__t*(self.__b - self.__a)
                   self.__qCal = self.__fun(self.__q)
           step += 1

       min_x = self.__p if self.__pCal < self.__qCal else self.__q
       min_y = self.__fun(min_x)
       result = {
           '极小值点为:': min_x,
           '极小值为:': min_y,
           '迭代次数为:': step,
           '|b-a|': abs(self.__b - self.__a),
           '|fun(b)-fun(a)|': abs(self.__fun(self.__b) - self.__fun(self.__a))
       }

       for key, value in result.items():
           print(key, value)

       self.__draw_image(min_x, min_y)

if __name__ == '__main__':
   a = 0
   b = 1
   epsilon = 1e-5
   deta = 1e-4
   Solution_golds('x**2 - math.sin(x)', a, b, epsilon, deta).run()



618.png