遺傳算法實戰1

命題: 來自不同地方的人們去同一個地方,需要安排他們的飛機行程,希望同一天在紐約會面,同時在同一天返回。要求總成本最小。

航班樣例數據如下:

起點,終點,起飛時間,到達時間,價格
LGA,OMA,6:19,8:13,239
OMA,LGA,6:11,8:31,249
LGA,OMA,8:04,10:59,136
OMA,LGA,7:39,10:24,219
LGA,OMA,9:31,11:43,210
OMA,LGA,9:15,12:03,99
LGA,OMA,11:07,13:24,171
OMA,LGA,11:08,13:07,175
LGA,OMA,12:31,14:02,234
OMA,LGA,12:18,14:56,172
LGA,OMA,14:05,15:47,226
OMA,LGA,13:37,15:08,250
LGA,OMA,15:07,17:21,129
OMA,LGA,15:03,16:42,135
LGA,OMA,16:35,18:56,144
OMA,LGA,16:51,19:09,147
LGA,OMA,18:25,20:34,205
OMA,LGA,18:12,20:17,242
LGA,OMA,20:05,21:44,172
OMA,LGA,20:05,22:06,261
……..

import time
import random
import numpy as np

#創建每個人對應的出發城市
people = [('Seymour', 'BOS'),
          ('Franny', 'DAL'),
          ('Zooey', 'CAK'),
          ('Walt', 'MIA'),
          ('Buddy', 'ORD'),
          ('Les', 'OMA')]
# 定義目的地
destination = 'LGA'

#定義一個函數解析原始數據txt文件
flights = {}
def extract_file(filename):
    flights = {}
    for line in open(filename):
        origin, dest, depart, arrive, price = line.strip().split(',')
        flights.setdefault((origin, dest), [])
        flights[(origin, dest)].append((depart, arrive, int(price)))
    return flights
flights = extract_file('schedule.txt')
list(flights.keys())[0]  

Out[277]: (‘LGA’, ‘DAL’)

[python3不支持字典索引,先轉成list]
(http://blog.csdn.net/a070220106/article/details/9089379)

#定義一個方便獲取分鐘數的函數
def getminutes(t):
    x = time.strptime(t, '%H:%M')
    return x[3] * 60 + x[4]

#定義打印出題解對應的人員航班信息的函數
def printschedule(r):
    for d in range(int(len(r) / 2)):
        name = people[d][0]
        origin = people[d][1]
        out = flights[(origin, destination)][(r[d])]
        ret = flights[(destination, origin)][(r[d + 1])]
        print('%10s%10s %5s-%5s $%3s %5s-%5s $%3s' % (name, origin,
                                                      out[0], out[1], out[2],                                                      ret[0], ret[1], ret[2]))

s = [1, 4, 3, 2, 7, 3, 6, 3, 2, 4, 5, 3]
printschedule(s)

Seymour BOS 8:04-10:11 9512:0814:05 142
Franny DAL 12:19-15:25 34210:5114:16 256
Zooey CAK 10:53-13:36 1899:5812:56 249
Walt MIA 9:15-12:29 22516:5019:26 304
Buddy ORD 16:43-19:00 24610:3313:11 132
Les OMA 11:08-13:07 17515:0717:21 129

# 定義成本函數(是優化問題的核心)
def costf(sol):
    totalprice = 0
    latestarrival = 0
    earliestdep = 24 * 60

    for d in range(int(len(sol) / 2)):
        # Get the inbound and outbound flights
        origin = people[d][1]
        #print('origin', origin)
        outbound = flights[(origin, destination)][(sol[d])]
        #print('outbound', outbound)
        returnf = flights[(destination, origin)][(sol[d + 1])]
        #print('returnf', returnf)

        # Total price is the price of all outbound and return flights
        totalprice += outbound[2]
        totalprice += returnf[2]

        # Track the latest arrival and earliest departure
        if latestarrival < getminutes(outbound[1]): latestarrival = getminutes(outbound[1])
        if earliestdep > getminutes(returnf[0]): earliestdep = getminutes(returnf[0])

    # Every person must wait at the airport until the latest person arrives.
    # They also must arrive at the same time and wait for their flights.
    totalwait = 0
    for d in range(int(len(sol) / 2)):
        origin = people[d][1]
        outbound = flights[(origin, destination)][(sol[d])]
        returnf = flights[(destination, origin)][(sol[d + 1])]
        totalwait += latestarrival - getminutes(outbound[1])
        totalwait += getminutes(returnf[0]) - earliestdep

        # Does this solution require an extra day of car rental? That'll be $50!
    if latestarrival > earliestdep: totalprice += 50

    return totalprice + totalwait
# 定義遺傳算法
def geneticoptimize(domain, costf, popsize=50, step=1 ,
                    mutprob=0.2, elite=0.2, maxiter=100):
    #popsize:種羣大小
    #step: 變異步長(一個種羣中的單個DNA變異的步長)
    #mutprob:種羣的新成員由變異而非交叉得來的概率
    #elite:種羣中被認爲是最優解且被允許傳入下一代的部分
    #maxiter: 需要運行多少代就停止

    # 定義變異參數
    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:]

    # 定義交叉(或者叫配對)函數
    def crossover(r1, r2):
        i = random.randint(1, len(domain) - 2)
        return r1[0:i] + r2[i:]

    # 創建隨機初始種羣(50個不同的種羣)
    pop = []
    for i in range(popsize):
        vec = [random.randint(domain[i][0], domain[i][1])
               for i in range(len(domain))]
        pop.append(vec)

    #每一代勝出的個數(這裏定義了20%的上一代優勝者會進入下一代)
    topelite = int(elite * popsize)

    # 主循環
    for i in range(100): #range(maxiter)
        scores = [(costf(v), v) for v in pop] #計算50個初始種羣每個的成本及排列方式
        scores.sort() #按照成本從小到大排序
        ranked = [v for (s, v) in scores]

        #從純粹的勝出者開始
        pop = ranked[0:topelite] #只保留20%的初始種羣

        # 添加變異和配對後的勝出者
        while len(pop) < popsize:
            if random.random() < mutprob: 
                #random.random()產生0-1之間的一個隨機數
                #當這個隨機數小於變異概率係數0.2的時候,開始變異
                #也就意味着有20%的機率是突變

                #變異(基因在未配對之前突變) 
                c = random.randint(0, topelite)#指定那個位置的基因發生突變
                pop.append(mutate(ranked[c])) #添加突變的種羣到被選中的純粹勝出的10個種羣中
            else:

                # 配對(2個初始純粹勝出的種羣配對)
                #這裏有機會c1與c2是相同的
                c1 = random.randint(0, topelite)
                c2 = random.randint(0, topelite)
                pop.append(crossover(ranked[c1], ranked[c2]))

        # 打印當前最佳的最小成本函數
        print(scores[0][0])

    #返回成本函數對應的解列表
    return scores[0][1] 

算法解釋

標準的題解數據格式應該是一個長度爲12的一元列表
e.g: sol = [8, 0, 3, 5, 4, 8, 5, 4, 8, 9, 4, 1]
定義一個與原始題解相同長度的二元列表,這樣子定義的目的是每天都有往返10趟航班,每個人往返都可以從這10趟航班中任意選擇,有利創造出任意的初始種題解
domain = [(0, 9)] * len(people) * 2

[(0, 9),
(0, 9),
(0, 9),
(0, 9),
(0, 9),
(0, 9),
(0, 9),
(0, 9),
(0, 9),
(0, 9),
(0, 9),
(0, 9)]

  • 1,創建初始種羣

pop :創建50個初始種羣的題解

for i in range(popsize):
    vec = [random.randint(domain[i][0], domain[i][1])
           for i in range(len(domain))]
    pop.append(vec)

pop
Out[301]:
[[0, 5, 6, 6, 6, 4, 7, 2, 3, 6, 7, 6],
[8, 9, 3, 1, 1, 8, 2, 7, 4, 2, 5, 8],
[0, 2, 0, 5, 4, 4, 8, 4, 1, 4, 0, 3],
[4, 6, 3, 5, 3, 6, 7, 5, 9, 4, 5, 9],
[4, 5, 6, 6, 5, 2, 8, 6, 5, 9, 9, 8],
[0, 9, 9, 8, 9, 1, 8, 8, 5, 1, 7, 7],
[2, 4, 8, 2, 5, 4, 8, 0, 0, 4, 8, 1],
[7, 8, 8, 7, 9, 5, 9, 5, 5, 5, 0, 9],
[0, 6, 1, 0, 5, 5, 5, 9, 0, 1, 6, 0],
[3, 8, 1, 7, 7, 5, 3, 0, 7, 7, 4, 5],
[4, 4, 9, 3, 2, 9, 7, 8, 6, 2, 0, 2],
[4, 9, 4, 8, 5, 9, 0, 6, 9, 8, 8, 5],
[9, 6, 6, 1, 2, 1, 4, 8, 8, 1, 5, 1],
[9, 9, 9, 9, 0, 8, 0, 1, 1, 8, 5, 7],
[7, 2, 7, 6, 5, 0, 8, 8, 1, 8, 1, 1],
[0, 6, 5, 9, 2, 7, 0, 8, 7, 0, 9, 7],
[1, 3, 4, 9, 8, 2, 9, 1, 0, 8, 2, 3],
[4, 3, 4, 9, 7, 2, 4, 7, 0, 7, 2, 2],
[8, 6, 6, 1, 8, 7, 6, 5, 8, 5, 2, 9],
[1, 8, 0, 1, 8, 1, 4, 5, 0, 4, 9, 1],
[1, 0, 2, 3, 0, 1, 4, 0, 1, 6, 2, 2],
[8, 1, 9, 0, 7, 9, 1, 2, 4, 2, 9, 3],
[4, 7, 8, 4, 6, 8, 8, 4, 5, 4, 5, 7],
[7, 1, 1, 0, 7, 2, 0, 0, 9, 6, 5, 5],
[0, 5, 4, 8, 9, 9, 9, 3, 4, 9, 7, 1],
[0, 9, 9, 4, 6, 8, 3, 8, 7, 1, 2, 2],
[6, 2, 3, 7, 3, 7, 5, 7, 4, 9, 5, 4],
[3, 4, 8, 7, 8, 6, 9, 4, 8, 2, 2, 6],
[4, 6, 8, 8, 7, 7, 1, 6, 0, 7, 5, 5],
[7, 3, 6, 0, 4, 9, 3, 5, 8, 7, 7, 7],
[5, 9, 9, 8, 1, 1, 1, 0, 0, 3, 9, 4],
[5, 8, 8, 9, 8, 6, 8, 8, 8, 1, 6, 5],
[6, 0, 7, 5, 9, 9, 1, 6, 7, 6, 4, 8],
[8, 7, 4, 8, 3, 9, 2, 9, 8, 7, 1, 7],
[9, 9, 2, 8, 6, 6, 3, 8, 8, 9, 8, 2],
[1, 4, 6, 5, 9, 5, 6, 5, 3, 6, 4, 7],
[1, 8, 3, 7, 3, 3, 3, 6, 7, 9, 6, 3],
[8, 3, 2, 9, 2, 5, 0, 7, 4, 0, 9, 8],
[8, 3, 0, 0, 5, 2, 3, 4, 9, 4, 6, 5],
[5, 0, 1, 3, 8, 8, 6, 9, 5, 0, 8, 9],
[1, 4, 3, 3, 3, 0, 5, 2, 9, 5, 7, 4],
[2, 4, 0, 2, 4, 8, 5, 5, 7, 0, 3, 9],
[0, 7, 5, 5, 1, 0, 2, 4, 7, 8, 0, 7],
[5, 4, 4, 4, 5, 7, 7, 7, 2, 8, 4, 9],
[3, 5, 4, 1, 7, 2, 5, 1, 4, 8, 5, 7],
[4, 9, 8, 8, 0, 0, 1, 5, 5, 9, 1, 2],
[3, 1, 0, 5, 7, 9, 4, 4, 0, 2, 9, 1],
[8, 8, 3, 5, 1, 7, 7, 9, 1, 8, 8, 1],
[5, 9, 6, 7, 6, 9, 9, 0, 4, 7, 7, 9],
[8, 0, 3, 5, 4, 8, 5, 4, 8, 9, 4, 1]]

  • 2,topelite, 對初始種羣排序,選擇前20%的初始種羣作爲下一代
    通過選拔後,第二代只剩下了10個種羣
for i in range(100): #range(maxiter)
        scores = [(costf(v), v) for v in pop] #計算50個初始種羣每個的成本及排列方式
        scores.sort() #按照成本從小到大排序
        ranked = [v for (s, v) in scores]

        #從純粹的勝出者開始
        pop = ranked[0:topelite] #只保留20%的初始種羣`

pop #10個種羣
Out[453]:
Out[453]:
[[6, 2, 2, 1, 5, 2, 6, 5, 9, 8, 3, 5],
[6, 8, 5, 6, 5, 7, 4, 3, 5, 7, 1, 2],
[3, 3, 5, 6, 3, 5, 7, 4, 4, 3, 7, 0],
[0, 6, 3, 4, 4, 5, 4, 2, 9, 4, 6, 7],
[9, 9, 8, 7, 8, 5, 6, 3, 9, 1, 5, 5],
[8, 4, 4, 3, 3, 3, 9, 6, 5, 5, 6, 6],
[2, 0, 5, 4, 5, 6, 2, 0, 8, 8, 7, 3],
[8, 3, 7, 6, 5, 1, 2, 9, 4, 2, 3, 8],
[2, 6, 2, 5, 7, 3, 8, 9, 1, 9, 9, 6],
[5, 8, 5, 7, 7, 4, 8, 5, 4, 3, 9, 2]]

  • 3,在第二代種羣的基礎上進行變異(這裏定義了20%的機會突變)或者配對,迭代100次,每次迭代要麼發生變異,要麼配對,變異或者配對。每次迭代總會保留初始種羣(這裏是50)的20%的個體進入下一代,然後繼續有20%的機率突變,或者80%的機率配對。達到設定的迭代次數後,選擇成本函數最小的一個題解,算法停止。

第一次迭代的時候,會從50個隨機初始種羣中產生10個種羣,然後突變一個或者配對產生一個,第二代總共11個種羣時候的pop變量:
(這裏出來的結果顯示是突變還是配對?)
觀察最後的一個結果就是突變或者配對產生新的一個後代,它跟倒數第三個前面一段類似,後面一段跟順數第四個一樣,所以是配對了的)
Out[459]:
[[6, 2, 2, 1, 5, 2, 6, 5, 9, 8, 3, 5],
[6, 8, 5, 6, 5, 7, 4, 3, 5, 7, 1, 2],
[3, 3, 5, 6, 3, 5, 7, 4, 4, 3, 7, 0],
[0, 6, 3, 4, 4, 5, 4, 2, 9, 4, 6, 7],
[9, 9, 8, 7, 8, 5, 6, 3, 9, 1, 5, 5],
[8, 4, 4, 3, 3, 3, 9, 6, 5, 5, 6, 6],
[2, 0, 5, 4, 5, 6, 2, 0, 8, 8, 7, 3],
[8, 3, 7, 6, 5, 1, 2, 9, 4, 2, 3, 8],
[2, 6, 2, 5, 7, 3, 8, 9, 1, 9, 9, 6],
[5, 8, 5, 7, 7, 4, 8, 5, 4, 3, 9, 2],
[2, 6, 2, 5, 7, 3, 4, 2, 9, 4, 6, 7]]

11個種羣對應的成本函數排序

scores
Out[462]:
[(4802, [6, 2, 2, 1, 5, 2, 6, 5, 9, 8, 3, 5]),
(4931, [6, 8, 5, 6, 5, 7, 4, 3, 5, 7, 1, 2]),
(4952, [3, 3, 5, 6, 3, 5, 7, 4, 4, 3, 7, 0]),
(5019, [0, 6, 3, 4, 4, 5, 4, 2, 9, 4, 6, 7]),
(5098, [9, 9, 8, 7, 8, 5, 6, 3, 9, 1, 5, 5]),
(5151, [8, 4, 4, 3, 3, 3, 9, 6, 5, 5, 6, 6]),
(5194, [2, 6, 2, 5, 7, 3, 4, 2, 9, 4, 6, 7]),
(5223, [2, 0, 5, 4, 5, 6, 2, 0, 8, 8, 7, 3]),
(5423, [8, 3, 7, 6, 5, 1, 2, 9, 4, 2, 3, 8]),
(5519, [2, 6, 2, 5, 7, 3, 8, 9, 1, 9, 9, 6]),
(5709, [5, 8, 5, 7, 7, 4, 8, 5, 4, 3, 9, 2])]

經過對此迭代後,每次總保留10個pop進行變異或者配對,迭代完成後,返回成本最小的一個結果作爲最終輸出。
最終的題解以及最小的成本在迭代12次就得到了。。

s = geneticoptimize(domain, costf)
[5, 3, 5, 3, 4, 3, 3, 0, 0, 0, 0, 0] #題解
4288
3901
3646
3646
3646
3490
3337
3303
2994
2994
2994
2912
2912
2912
2912
2912
2912
2912
2912
2912
2912
2912
2912

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章