1. 程式人生 > >集體智慧程式設計5-優化演算法-爬山法、模擬退火、遺傳演算法

集體智慧程式設計5-優化演算法-爬山法、模擬退火、遺傳演算法

最優化演算法的思想在於,我們往往並不需要得到最優解,而是得到一個近似最優解,來節省時間的開銷。

圖例
* 隨機演算法
為了解決遍歷引發的時間問題,有時候在沒有嚴格要求的情況下,可以通過隨機去一定的點,比較這些取的點數,總能找到一個近似最優解的情況。

  • 爬山演算法
    隨機演算法沒有邏輯可尋,成本較低,但是效果較差。而爬山演算法利用了資料的內在規律。就像爬山一樣,為了,爬到上的最頂部,在到達一個點後,我們總是環顧四周,尋找比當前位置高的地方,然後繼續往上爬。即比較當前位置的鄰近值。

  • 模擬退火
    很顯然,爬山演算法有個缺陷,比如,如果我們的當前位置是E,當取A,D兩個值作比較時,會排除D點,這樣結果將陷入到區域性最大值A。為了避免在最開始就陷入區域性最大值,所以,我們引入了一個概率PP = exp[-(newcost - oldcost)/ T ]

    ,即在溫度很高時或者之後的值與第一個值相差不大時,會更可能不排除在外。越往後,隨著溫度的降低,將越來越接近爬山演算法。這樣在最初就不會排除D,從而可以順利找到B

  • 遺傳演算法
    遺傳演算法類似於人類進化論,即物種總是朝著最優秀的方向進化,進化的方式有重組和變異,重組及優秀個體交換資訊,變異即優秀個體內部改變元素。選擇即我們通過成本函式進行排序,選擇更好的值。

""" 
@author: zoutai
@file: optimization.py 
@time: 2018/04/22 
@description: 
"""

import random
import time

import math

people = [('Seymour'
, 'BOS'), ('Franny', 'DAL'), ('Zooey', 'CAK'), ('Walt', 'MIA'), ('Buddy', 'ORD'), ('Les', 'OMA')] # newyork的Laguardia機場 destination = 'LGA' # 第一步,以出發地-目的地為key,以具體的航班資訊為value,做字典對映 flights = {} for line in open('schedule.txt'): origin, dest, departTime, arriveTime, price = line.strip().split(','
) flights.setdefault((origin, dest), []) # 多趟航班,使用append flights[(origin, dest)].append((departTime, arriveTime, int(price))) # 返回時間的分鐘表示 def getminutes(t): x = time.strptime(t, '%H:%M') return x[3] * 60 + x[4] def printschedule(r): for d in range(len(r) // 2): name = people[d][0] origin = people[d][1] go = flights[(origin, dest)][r[d * 2]] back = flights[(dest, origin)][r[d * 2 + 1]] print('%10s%10s,%5s-%5s,%3s,%5s-%5s,%3s' % (name, origin, go[0], go[1], go[2], back[0], back[1], back[2])) s = [1, 4, 3, 2, 7, 3, 6, 3, 2, 4, 5, 3] printschedule(s) # 成本函式=等待時間+機票+計程車 def schedulecost(sol): totalcost = 0 earliestDep = 24 * 60 latestArrive = 0 for d in range(len(sol) // 2): name = people[d][0] origin = people[d][1] go = flights[(origin, dest)][int(sol[d * 2])] back = flights[(dest, origin)][sol[int(d * 2 + 1)]] totalcost += go[2] + back[2] if latestArrive < getminutes(go[1]): latestArrive = getminutes(go[1]) if earliestDep > getminutes(back[0]): earliestDep = getminutes(back[0]) totalWait = 0 for d in range(len(sol) // 2): go = flights[(origin, dest)][sol[d * 2]] back = flights[(origin, dest)][sol[d * 2 + 1]] totalWait += (latestArrive - getminutes(go[1])) totalWait += (getminutes(back[0]) - earliestDep) # 計程車50/天 if latestArrive < earliestDep: totalcost += 50 return totalcost + totalWait print("預設初始化值:",schedulecost(s)) domain = [(0, 9)] * (len(people) * 2) # 1、隨機法 def randomoptimize(domain, costf): best = 999999999 # bestRs = None iterNum = 1000 for i in range(iterNum): r = [random.randint(domain[i][0], domain[i][1]) for i in range(len(domain))] cost = costf(r) if cost < best: best = cost # bestRs = best return r, best r, best = randomoptimize(domain,schedulecost) print("隨機法:",best) # 2、爬山法 # 對於每一個未知數,搜尋維度方向上的鄰近節點,取最小值,直到最小值不變,退出 def hillClimb(domain,costf): # 建立隨機解-初始化 sol = [random.randint(domain[i][0],domain[i][1]) for i in range(len(domain))] # 迴圈 while 1: # 建立鄰接-表:二維的,即左右兩個 neighbors = [] # 這裡面的鄰接區並不完全對,應該再加上一個維度迴圈,即單獨固定一個變數變化 for j in range(len(domain)): if sol[j]>domain[j][0]: neighbors.append(sol[0:j]+[sol[j]-1]+sol[j+1:]) if sol[j]<domain[j][1]: neighbors.append(sol[0:j]+[sol[j]+1]+sol[j+1:]) # 比較當前值和鄰接值 current = costf(sol) best = current for j in range(len(neighbors)): cost = costf(neighbors[j]) if best>cost: best=cost sol = neighbors[j] # 整個鄰接區都沒有更好的,則終止迴圈 if best==current: break; return sol,best sol,best = hillClimb(domain,schedulecost) print("爬山法:",best) # 3、模擬退火 # 原理相對於爬山法,為了避免陷入區域性最小值,在初期的時候,對於不符合的結果,暫時不排除 # T:初始溫度,cool:冷卻因子,step方向步長 def annealingoptimize(domain,costf,T=10000,cool=0.95,step=1): # 初始化隨機值 vec = [random.randint(domain[i][0],domain[i][1]) for i in range(len(domain))] while T > 0.1: # 隨機選擇一個方向 i = random.randint(0,len(domain)-1) director = random.randint(-step,step) vecb = vec[:] vecb[i]+=director # 偏移 # 防止出界 if vecb[i]<domain[i][0]: vecb[i]=domain[i][0] elif vecb[i]>domain[i][1]: vecb[i]=domain[i][1] costCur = costf(vec) costB = costf(vecb) if (costB<costCur or random.random()<pow(math.e,-(costB-costCur)/T)): vec = vecb # 即便costB>costCur,也不用保留之前的vec,因為,溫度下降後,會再返回到當前值 # 降低溫度 T = T * cool return vec,costf(vec) sol,best = annealingoptimize(domain,schedulecost) print("模擬退火演算法:",best) # 4、遺傳演算法 def geneticoptimize(domain,costf,popsize=50,step=1,mutprob=0.2,elite=0.2,mixiter=100): # 變異 def mutate(vec): i = random.randint(0,len(domain)-1) # 隨機數什麼用? if random.random()<0.5 and vec[i]>domain[i][0]: return vec[:i]+[vec[i]-step]+vec[i+1:] elif vec[i]<domain[i][1]: return vec[:i]+[vec[i]+step]+vec[i+1:] # 重組 def crossover(vec1,vec2): i = random.randint(1,len(domain)-2) return vec1[:i]+vec2[i:] # 初始化種群 pop = [] for i in range(popsize): vec = [random.randint(domain[j][0],domain[j][1]) for j in range(len(domain))] pop.append(vec) toplite = int(popsize * elite) for i in range(mixiter): # 排序,進行物種進化選擇 scores = [(costf(v),v) for v in pop] scores.sort() ranked = [v for (s,v) in scores] pop = ranked[:toplite] while len(pop)<popsize: if random.random()<mutprob: c = random.randint(0,toplite) pop.append(mutate(ranked[c])) else: c1 = random.randint(0,toplite) c2 = random.randint(0,toplite) pop.append(crossover(ranked[c1],ranked[c2])) print(scores[0][0]) return scores[0][1],scores[0][0] sol, best = geneticoptimize(domain, schedulecost) print("遺傳演算法:",best)