集體智慧編程學習之最佳化系統

來源:互聯網
上載者:User

最佳化系統的想法真不好簡單地說的明白,這樣吧,我爸在陝西西安,我媽在安徽的合肥,我弟弟在深圳,打算坐飛機到北京我這裡玩。家人都比較節省,打算到了機場後互相等對方,然後一起坐車租車到我住的地方。

我查了qunar,一天從西安,合肥,深圳到北京有很多航班,怎麼樣讓總的票價最少,並且在機場互相等待的時間降到最低。這裡假設一個人等1分鐘相當於1塊錢,這樣我們的目標就是讓成本schedulcost=(父親機票錢+母親機票錢+弟弟機票錢+父親等候分鐘+母親等候分鐘+弟弟等候時間)最小,這裡面有可能父親,或者母親或者弟弟的等候分鐘是0。

先定義家長控制:

people = [('baba','XA'),          ('mama','HF'),          ('didi','sz')]

爸爸從西安出發,媽媽從合肥出發,弟弟從深圳出發;

再定義航班資訊:

flights = [('XA','BJ') : [('06:30', '08:30', 600), ('07:45', '10:15', 500)....],('HF','BJ') : [('06:45', '09:30', 900), ('07:25', '10:45', 800)....],('SZ','BJ') : [('06:25', '10:30', 1200), ('08:05', '12:10', 1300)....],]

從西安到北京的航班有:6:30出發,8:30到達,機票600元;07:45出發,10:15到達,機票500元。。。

從合肥到北京的航班有:6:45出發,9:30到達,機票900元;07:25出發,10:45到達,機票800元。。。

從深圳到北京的航班有:6:25出發,10:30到達,機票1200元;08:05出發,12:10到達,機票1300元。。。

下面假設航班是:

sol = [2,2,1]

從西安到北京的第二班,從合肥到北京的第二班,從西安到北京的第一班。

下面就可以算計我上面定的航班的花費了:其中

def schedulecost(sol):  totalprice=0  latestarrival=0  for d in range(len(sol)):    origin=people[d][1]    outbound=flights[(origin,'BJ')][int(sol[d])]        totalprice+=outbound[2]        if latestarrival<getminutes(outbound[1]): latestarrival=getminutes(outbound[1])    totalwait=0    for d in range(len(sol)):    origin=people[d][1]    outbound=flights[(origin,'BJ')][int(sol[d])]    totalwait+=latestarrival-getminutes(outbound[1])     return totalprice+totalwait

很好懂,shedulecost計算了總的航班機票錢和總的等候時間,

計算總的等候時候先找出最晚到達的一班飛機的時間latestarrival。

我們的目標:找出某種組合的sol,讓schedulecost成本最低;


因為一天內的航班有很多,並且我已經把問題簡化到最少,所以在現實情況中結果的組合數會輕鬆上億,

如果shedulecost稍微複雜的話,找出最低成本的方案會非常吃力,下面看看幾種可能的做法:

(一)隨機搜尋法

最簡單的,隨機計算其中的一部分結果,比如1000條可能的解,最終的結果取這部分計算結果中的最小值,這種演算法最簡單,問題也最明顯。

下面的實現,第一個參數是每種可能結果的範圍,比如從西安到北京一天有100個航班,從合肥到北京一天有80個航班,從深圳到北京一天有150個航班,那麼domain的定義就是domain=[(0,100),(0,80),(0,150)];第二個參數就是上面定義的schedulecost函數;

def randomoptimize(domain,costf):  best=999999999  bestr=None  for i in range(0,1000):    # Create a random solution    r=[float(random.randint(domain[i][0],domain[i][1]))        for i in range(len(domain))]        # Get the cost    cost=costf(r)        # Compare it to the best one so far    if cost<best:      best=cost      bestr=r   return r

一千次的嘗試可能是總解總很說得好的一部分,但是在多試幾次之後,也許會得到一個差不多的解;

隨機搜尋法的問題在於,隨機搜尋是跳躍性的,這次的計算和上次的計算之前沒有任何關係,也就是最新一次計算並沒能利用之前已發現的優解,下面看看幾種改進的演算法:


(二)類比爬山法
爬山法從一個隨機解開始,然後在其臨近的解中尋找更好的題解。在本例中,亦即找到所有相對於最初的隨機安排,能夠讓某個人乘坐的航班稍早或者稍晚一些的安排,沃恩對每一個相鄰的時間安排都進行成本計算,具有最低成本的安排將成為新的題解。重複這一過程知道沒有相鄰安排能夠改善成本為止:

def hillclimb(domain,costf):  # Create a random solution  sol=[random.randint(domain[i][0],domain[i][1])      for i in range(len(domain))]  # Main loop  while 1:    # Create list of neighboring solutions    neighbors=[]        for j in range(len(domain)):      # One away in each direction      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:])    # See what the best solution amongst the neighbors is    current=costf(sol)    best=current    for j in range(len(neighbors)):      cost=costf(neighbors[j])      if cost<best:        best=cost        sol=neighbors[j]    # If there's no improvement, then we've reached the top    if best==current:      break  return sol

之所以叫爬山法,就是我們沿著一條下坡路一直走到坡底,這個方法的速度很快,並且找到的結果一般會比隨機搜尋法好一些。但是也有個問題,這種演算法的假設是最優解就在初始下坡位置的波穀處,第一次的隨機解會影響最終的效果。如果初始所處的位置的坡穀不是所有坡穀中最低的,那這種演算法的結果不會得到最優解,所以這種演算法可以求出局部最優解,而不是全域最優解。


(三)類比退火法

退火指合金在加熱後再慢慢冷卻的過程。大量的原子因為受到激發而向周圍跳躍,然後又逐漸穩定到一個低能階的狀態。

有時候在找到最優解前轉向一個更差的解是有必要的。退火法一個隨機解開始,用一個變數表示溫度,溫度在最開始會很高,爾後慢慢變低。每一次迭代周期,演算法會隨機選中題解中的某個數字,然後朝某個方向變化。這個演算法和爬山法的區別是。爬山法每次的迭代都會朝成本最低的發展,而退火法不一定,有一定的比例選選擇出更差的解成為當前迭代題解,這是避免局部最優解的一種嘗試。

溫度在開始會比較高,也就有更高的機率會轉向更差的解,隨著迭代的進行,溫度越來越低,演算法越來越不能接受較差的解。

def annealingoptimize(domain,costf,T=10000.0,cool=0.95,step=1):  # Initialize the values randomly  vec=[float(random.randint(domain[i][0],domain[i][1]))        for i in range(len(domain))]    while T>0.1:    # Choose one of the indices    i=random.randint(0,len(domain)-1)    # Choose a direction to change it    dir=random.randint(-step,step)    # Create a new list with one of the values changed    vecb=vec[:]    vecb[i]+=dir    if vecb[i]<domain[i][0]: vecb[i]=domain[i][0]    elif vecb[i]>domain[i][1]: vecb[i]=domain[i][1]    # Calculate the current cost and the new cost    ea=costf(vec)    eb=costf(vecb)    p=pow(math.e,(-eb-ea)/T)    # Is it better, or does it make the probability    # cutoff?    if (eb<ea or random.random()<p):      vec=vecb          # Decrease the temperature    T=T*cool  return vec

T是初始溫度,cool是冷卻率,step是每次迭代的步伐。


(四)遺傳演算法

這種演算法是先隨機產生一組結果,我們成為種群。在最佳化的每次迭代中,演算法會計算整個種群中成本函數,從而得到一個有關題解的有序列表。在對題解排序後,我們來創造下一代種群:首先,當前種群中位於最頂端題解加入新種群,這是精英選拔,新種群的其他成員是修改最優解的變異而成。

變異有兩種方法,一種是對最優解進行微小的簡單的隨機的改變。在本例中,我們從最優解中隨機播放一個數字,然後對其遞增遞減即可。另一種方法稱為交叉或者配對。這種做法是選取最優解中的兩個解,然後講他們按照某種方式進行結合。在本例中,實現交叉的一種簡單的方式是,從一個解中隨機取出一個數字作為新題解的某個元素,剩餘元素則來自於另一個題解。

一個新種群是通過對最優解進行隨機的變異和配對處理構造出來的,它的大小通常和舊種群相同,爾後,這一過程會一直重複進行。新的種群進過排序,又一個種群被構造出來。達到指定的迭代次數或者連續經過數次迭代後結果都沒有得到改善,整個過程結束。

def geneticoptimize(domain,costf,popsize=50,step=1,                    mutprob=0.2,elite=0.2,maxiter=100):  # Mutation Operation  def mutate(vec):    i=random.randint(0,len(domain)-1)    if random.random()<0.5 and vec[i]>domain[i][0]:      return vec[0:i]+[vec[i]-step]+vec[i+1:]     elif vec[i]<domain[i][1]:      return vec[0:i]+[vec[i]+step]+vec[i+1:]    # Crossover Operation  def crossover(r1,r2):    i=random.randint(1,len(domain)-2)    return r1[0:i]+r2[i:]  # Build the initial population  pop=[]  for i in range(popsize):    vec=[random.randint(domain[i][0],domain[i][1])          for i in range(len(domain))]    pop.append(vec)    # How many winners from each generation?  topelite=int(elite*popsize)    # Main loop   for i in range(maxiter):    scores=[(costf(v),v) for v in pop]    scores.sort()    ranked=[v for (s,v) in scores]        # Start with the pure winners    pop=ranked[0:topelite]        # Add mutated and bred forms of the winners    while len(pop)<popsize:      if random.random()<mutprob:        # Mutation        c=random.randint(0,topelite)        pop.append(mutate(ranked[c]))      else:              # Crossover        c1=random.randint(0,topelite)        c2=random.randint(0,topelite)        pop.append(crossover(ranked[c1],ranked[c2]))        # Print current best score    print scores[0][0]      return scores[0][1]

popsize指的是種群大小,mutprob指的是新成員是由變異或者交叉而來的機率,elite值得是種群中可以遺傳到下一代的部分,maxiter迭代的最大次數。


代碼還是很簡單的,等下次多搞點資料,我來試試效果。。。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.