# -*- coding: UTF-8 -*- from math import * import numpy as np ############################################ # 黄金分割法(与えられた方向での最小値) # a,b : 初期区間 a < b # eps : 許容誤差 # val : 関数値 # ind : 計算状況 # >= 0 : 正常終了(収束回数) # = -1 : 収束せず # max : 最大試行回数 # w : 位置 # dw : 傾きの成分 # snx : 関数値を計算する関数の名前 # return : 結果(w+y*dwのy) # coded by Y.Suganuma ############################################ def gold(a, b, eps, val, ind, max, w, dw, snx) : # 初期設定 tau = (sqrt(5.0) - 1.0) / 2.0 x = 0.0 count = 0 ind[0] = -1 x1 = b - tau * (b - a) x2 = a + tau * (b - a) f1 = snx(x1, w, dw) f2 = snx(x2, w, dw) # 計算 while count < max and ind[0] < 0 : count += 1 if f2 > f1 : if abs(b-a) < eps : ind[0] = 0 x = x1 val[0] = f1 else : b = x2 x2 = x1 x1 = a + (1.0 - tau) * (b - a) f2 = f1 f1 = snx(x1, w, dw) else : if abs(b-a) < eps : ind[0] = 0 x = x2 val[0] = f2 f1 = f2 else : a = x1 x1 = x2 x2 = b - (1.0 - tau) * (b - a) f1 = f2 f2 = snx(x2, w, dw) # 収束した場合の処理 if ind[0] == 0 : ind[0] = count fa = snx(a, w, dw) fb = snx(b, w, dw) if fb < fa : a = b fa = fb if fa < f1 : x = a val[0] = fa return x ############################################ # DFP法 or BFGS法 # method : =0 : DFP法 # =1 : BFGS法 # opt_1 : =0 : 1次元最適化を行わない # =1 : 1次元最適化を行う # max : 最大繰り返し回数 # n : 次元 # eps : 収束判定条件 # step : きざみ幅 # y : 最小値 # x1 : x(初期値と答え) # dx : 関数の微分値 # H : Hesse行列の逆行列 # snx : 関数値を計算する関数名 # dsnx : 関数の微分を計算する関数名(符号を変える) # return : >=0 : 正常終了(収束回数) # =-1 : 収束せず # =-2 : 1次元最適化の区間が求まらない # =-3 : 黄金分割法が失敗 # coded by Y.Suganuma ############################################ def DFP_BFGS(method, opt_1, max, n, eps, step, y, x1, dx, H, snx, dsnx) : count = 0 sw = 0 sw1 = np.empty(1, np.int) y2 = np.empty(1, np.float) x2 = np.empty(n, np.float) g = np.empty(n, np.float) g1 = np.empty(n, np.float) g2 = np.empty(n, np.float) s = np.empty(n, np.float) w = np.empty(n, np.float) I = np.identity(n, np.float) H1 = np.empty((n, n), np.float) H2 = np.empty((n, n), np.float) y1 = snx(0.0, x1, dx) dsnx(x1, g1) while count < max and sw == 0 : count += 1 # 方向の計算 for i1 in range(0, n) : dx[i1] = 0.0 for i2 in range(0, n) : dx[i1] -= H[i1][i2] * g1[i2] # 1次元最適化を行わない if opt_1 == 0 : # 新しい点 for i1 in range(0, n) : x2[i1] = x1[i1] + step * dx[i1] # 新しい関数値 y2[0] = snx(0.0, x2, dx) # 1次元最適化を行う else : # 区間を決める sw1[0] = 0 f1 = y1 sp = step f2 = snx(sp, x1, dx) if f2 > f1 : sp = -step for i1 in range(0, max) : f2 = snx(sp, x1, dx) if f2 > f1 : sw1[0] = 1 break else : sp *= 2.0 f1 = f2 # 区間が求まらない if sw1[0] == 0 : sw = -2 # 区間が求まった else : # 黄金分割法 k = gold(0.0, sp, eps, y2, sw1, max, x1, dx, snx) # 黄金分割法が失敗 if sw1[0] < 0 : sw = -3 # 黄金分割法が成功 else : # 新しい点 for i1 in range(0, n) : x2[i1] = x1[i1] + k * dx[i1] if sw == 0 : # 収束(関数値の変化<eps) if abs(y2[0]-y1) < eps : sw = count y[0] = y2[0] for i1 in range(0, n) : x1[i1] = x2[i1] # 関数値の変化が大きい else : # 傾きの計算 dsnx(x2, g2) sm = 0.0 for i1 in range(0, n) : sm += g2[i1] * g2[i1] sm = sqrt(sm) # 収束(傾き<eps) if sm < eps : sw = count y[0] = y2[0] for i1 in range(0, n) : x1[i1] = x2[i1] # 収束していない else : for i1 in range(0, n) : g[i1] = g2[i1] - g1[i1] s[i1] = x2[i1] - x1[i1] sm1 = 0.0 for i1 in range(0, n) : sm1 += s[i1] * g[i1] # DFP法 if method == 0 : for i1 in range(0, n) : w[i1] = 0.0 for i2 in range(0, n) : w[i1] += g[i2] * H[i2][i1] sm2 = 0.0 for i1 in range(0, n) : sm2 += w[i1] * g[i1] for i1 in range(0, n) : w[i1] = 0.0 for i2 in range(0, n) : w[i1] += H[i1][i2] * g[i2] for i1 in range(0, n) : for i2 in range(0, n) : H1[i1][i2] = w[i1] * g[i2] for i1 in range(0, n) : for i2 in range(0, n) : H2[i1][i2] = 0.0 for i3 in range(0, n) : H2[i1][i2] += H1[i1][i3] * H[i3][i2] for i1 in range(0, n) : for i2 in range(0, n) : H[i1][i2] = H[i1][i2] + s[i1] * s[i2] / sm1 - H2[i1][i2] / sm2 # BFGS法 else : for i1 in range(0, n) : for i2 in range(0, n) : H1[i1][i2] = I[i1][i2] - s[i1] * g[i2] / sm1 for i1 in range(0, n) : for i2 in range(0, n) : H2[i1][i2] = 0.0 for i3 in range(0, n) : H2[i1][i2] += H1[i1][i3] * H[i3][i2] for i1 in range(0, n) : for i2 in range(0, n) : H[i1][i2] = 0.0 for i3 in range(0, n) : H[i1][i2] += H2[i1][i3] * H1[i3][i2] for i1 in range(0, n) : for i2 in range(0, n) : H[i1][i2] += s[i1] * s[i2] / sm1 y1 = y2[0] for i1 in range(0, n) : g1[i1] = g2[i1] x1[i1] = x2[i1] if sw == 0 : sw = -1 return sw ---------------------------------- # -*- coding: UTF-8 -*- import numpy as np import sys from math import * from function import DFP_BFGS ############################################ # 準Newton法によるの最小値の計算 # coded by Y.Suganuma ############################################ ############################################ # 与えられた点xから,dx方向へk*dxだけ進んだ # 点における関数値を計算する ############################################ # 関数1 def snx1(k, x, dx) : w = np.empty(2, np.float) w[0] = x[0] + k * dx[0] w[1] = x[1] + k * dx[1] x1 = w[0] - 1.0 y1 = w[1] - 2.0 y = x1 * x1 + y1 * y1 return y # 関数2 def snx2(k, x, dx) : w = np.empty(2, np.float) w[0] = x[0] + k * dx[0] w[1] = x[1] + k * dx[1] x1 = w[1] - w[0] * w[0] y1 = 1.0 - w[0] y = 100.0 * x1 * x1 + y1 * y1 return y # 関数3 def snx3(k, x, dx) : w = np.empty(2, np.float) w[0] = x[0] + k * dx[0] w[1] = x[1] + k * dx[1] x1 = 1.5 - w[0] * (1.0 - w[1]) y1 = 2.25 - w[0] * (1.0 - w[1] * w[1]) z1 = 2.625 - w[0] * (1.0 - w[1] * w[1] * w[1]) y = x1 * x1 + y1 * y1 + z1 * z1 return y ############################################ # 微係数を計算する ############################################ # 関数1 def dsnx1(x, dx) : dx[0] = -2.0 * (x[0] - 1.0) dx[1] = -2.0 * (x[1] - 2.0) # 関数2 def dsnx2(x, dx) : dx[0] = 400.0 * x[0] * (x[1] - x[0] * x[0]) + 2.0 * (1.0 - x[0]) dx[1] = -200.0 * (x[1] - x[0] * x[0]) # 関数3 def dsnx3(x, dx) : dx[0] = 2.0 * (1.0 - x[1]) * (1.5 - x[0] * (1.0 - x[1])) + 2.0 * (1.0 - x[1] * x[1]) * (2.25 - x[0] * (1.0 - x[1] * x[1])) + 2.0 * (1.0 - x[1] * x[1] * x[1]) * (2.625 - x[0] * (1.0 - x[1] * x[1] * x[1])) dx[1] = -2.0 * x[0] * (1.5 - x[0] * (1.0 - x[1])) - 4.0 * x[0] * x[1] * (2.25 - x[0] * (1.0 - x[1] * x[1])) - 6.0 * x[0] * x[1] * x[1] * (2.625 - x[0] * (1.0 - x[1] * x[1] * x[1])) # データの入力 sw = 0 line = sys.stdin.readline() s = line.split() fun = int(s[1]) n = int(s[3]) max = int(s[5]) opt_1 = int(s[7]) line = sys.stdin.readline() s = line.split() method = int(s[1]) eps = float(s[3]) step = float(s[5]) x = np.empty(n, np.float) dx = np.empty(n, np.float) H = np.identity(n, np.float) y = np.empty(1, np.float) line = sys.stdin.readline() s = line.split() for i1 in range(0, n) : x[i1] = float(s[i1+1]) # 実行 if fun == 1 : sw = DFP_BFGS(method, opt_1, max, n, eps, step, y, x, dx, H, snx1, dsnx1) elif fun == 2 : sw = DFP_BFGS(method, opt_1, max, n, eps, step, y, x, dx, H, snx2, dsnx2) else : sw = DFP_BFGS(method, opt_1, max, n, eps, step, y, x, dx, H, snx3, dsnx3) # 結果の出力 if sw < 0 : print(" 収束しませんでした!", end="") if sw == -1 : print("(収束回数)") elif sw == -2 : print("(1次元最適化の区間)") else : print("(黄金分割法)") else : print(" 結果=", end="") for i1 in range(0, n) : print(str(x[i1]) + " ", end="") print(" 最小値=" + str(y[0]) + " 回数=" + str(sw))