集体智慧编程-第五章优化

喜夏-厌秋 提交于 2020-02-24 23:27:58

组团旅游

import time
import random
import math
people=[('seymour','BOS'),
        ('FRANNY','DAL'),
        ('ZOOEY','CAK'),
        ('WALT','MIA'),
        ('buddy','ORD'),
        ('LES','OMA')]
destination='LGA'

航班数据schedule.txt

起点,终点,起飞时间,达到时间,价格

加载数据的代码optimization.py

flights={}
for line in file('schedule.txt'):
    origin,dest,depart,arrive,price=line.strip().split(',')
    #key:(origin,dest) value:(depart,arrive,price)
    flights.setdefault((origin,dest),[])
    flights[(origin,dest)].append((depart,arrive,int(price)))
def getminutes(t):
    x=time.strptime(t,'%H:%M')
    return x[3]*60+x[4]
    #x[3]为小时数;x[4]为分钟数

描述题解

r=[1,4,3,2,7,3,6,3,2,4,5,3]
def printschedule(r):
    for d in range(len(r)/2):
        name=people[d][0]
        origin=people[d][1]
        out=flights[(origin,destination)][r[2*d]]
        ret=flights[(destination,origin)][r[2*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])

成本函数

返回值越小越好

该函数考察了总的旅行成本以及不同的家庭成员在机场的总的等待时间。如果汽车在租用时间后归还,还会追加50美元罚款。

sol=r
def schedulecost(sol):
    totalprice=0
    latestarrival=0
    earliestdep=24*60
    for d in range(len(sol)/2):
        origin=people[d][1]
        
        outbound=flights[(origin,desitination)][int(sol[2*d])]
        returnf=flights[(desitination,origin)][int(sol[2*d+1])]
        #总价格:往返航班
        totalprice+=outbound[2]
        totalprice+=returnf[2]
        #记录最晚达到时间和最早离开时间
        if latestarrival<getminutes(outbound[1]):latestarrival=getminutes(outbound[1])
        if earliestdep>getminutes(returnf[0]):earliestdep=getminutes(returnf[0])
    #每个人必须在机场等待直到最后一个人达到
    #也必须在相同的时间内达到,并等候他们的返程航班
    totalwait=0
    for d in range(len(sol)/2):
        origin=people[d][1]
        outbound=flights[(origin,desitination)][int(sol[2*d])]
        returnf=flights[(desitination,origin)][int(sol[2*d+1])]
        totalwait+=latestarrival-getminutes(outbound[1])
        totalwait+=getminutes(returnf[0])-earliestdep
    if latestarrival>earliestdep: totalprice+=50
    return totalprice+totalwait

随机搜索

它是我们评估其他算法优劣的基线。

domain为每个人往返航班的航班号的总和,航班号为(0,9)之间的数。

domain=[(0,9)]*(len(optimization.people)*2)
def randomoptimize(domain,costf):
    best=99999999
    bestr=None
    for i in range(1000):
    #r=[1,4,3,2,7,3,6,3,2,4,5,3]
       r=[random.randint(domain[i][0],domain[i][1]) for i in range(len(domain))]
       cost=costf(r)
       if cost<best:
          best=cost
          bestr=r
    return r

爬山法

随机尝试各种题解是非常低效的,没有充分利用已经发现的最优解。
先从一个随机的旅行安排方案开始,然后再找到所有与之相邻的安排,亦即找到所有相对于最初的随机安排,能够让每个人乘坐的航班稍早或稍晚一些的安排。我们对相邻的时间安排都进行成本计算,就把最低成本的安排将成为新的题解。直至没有安排能够改善成本为止。

domain为每个人往返航班的航班号的总和,航班号为(0,9)之间的数。

def hillclimb(domain,costf):
    #创建一个随机解SOL类似r
    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]:
          #传入的是一串r,只是改动了sol[j]
              neighbors.append(sol[0:j]+[sol[j]-1]+sol[j+1:])
          if sol[j]<domain[j][0]:
          #传入的是一串r,只是改动了sol[j]
              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 cost<best:
                best=cost
                sol=neighbors[j]
            if best==current:
                break
    return sol

输出的sol为最优的初始化r航班表

sol=optimization.hillclimb(domain,optimization.schedulecost)
optimization.schedulecost(sol)
#将航班安排表打印
#sol=[1,4,3,2,7,3,6,3,2,4,5,3]
optimization.printschedule(sol)

最终结果可能是局部最优不是全局最优解。

模拟退火法

可以避免陷入局部最优解。

在某些情况下,在我们能够得到一个更优的解之前转向一个更差的解释很有必要的。模拟退火算法不仅因为它会接受一个更优的解,还会因为它在退火过程开始阶段会接受表现较差的值。随着退火的不断进行,算法越来越不可能接受较差的解,直到最后,它只会接受更优的解。
该算法只会倾向于一个稍差的解而不会是个非常差的解。

def annealingoptimize(domain,costf,T=10000,cool=0.95.step=1):
     #随机初始化值
     vec=[float(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)
         #选择一个改变索引的方向
         dir=random.randint(-step,step)
         
         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][0]
         
         ea=costf(vec)
         eb=costf(vecb)
         
         if(eb<ea or random.random()<pow(math.e,-(eb-ea)/T))
                vec=vecb
         T=T*cool
    return vec
sol=optimization.annealingoptimize(domain,optimization.schedulecost)
optimization.schedulecost(sol)
#将航班安排表打印
#sol=[1,4,3,2,7,3,6,3,2,4,5,3]
optimization.printschedule(sol)

遗传算法

先随机生成一组解,我们称为种群。

在优化的每一步,算法会计算整个种群的成本函数,从而得到一个有关题解的有序列表。

在对题解进行排序后,一个新的种群被创建出来。

我们将当前题解中位于最顶端的题解加入到其所在的新种群中。称为精英选拔法。

新种群的余下部分是由修改最优解后形成的全新解组成的。
修改题解有两个方法:
变异:改变某个数字。
交叉:按照某种方式进行组合。

重复进行

def geneticoptimize(domain,costf,popsize=50,size=1,mutprob=1,mutprob=0.2,elite=0.2,maxiter=100):
    #变异操作,改变某个数字
    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.random(1,len(domain)-2)
        return r1[0:i]+r2[i:]
    #构造初始种群
    pop=[]
    for i in range(popsize):
        vec=[random.randint(domain[i][0],domain[i][1]) for i in range(len(domain))]
        pop.append(vec)
    #优胜者
    topelite=int(elite*popsize)
    #主循环
    for i in range(maxiter):
         scores=[(costf(v),v) for v in pop]
         scores.sort()
         ranked=[v for (s,v) in scores]
         
         pop=ranked[0:topelite]
         #添加变异和配对后的胜出者
         while len(pop)<popsize:
             if random.randint()<mutprob:
                 c=random.random(0,topelite)
                 pop.append(mutate(ranked[c]))
             else:#交叉
                c1=random.randint(0,topelite)
                c2=random.random(0,topelite)
                pop.append(crossover(ranked[c1],ranked[c2]))
    #打印当前最优值,cost
         print scores[0][0]
    return scores[0][1]#打印种群,即最优的排班表

popsize:种群大小

mutprob:种群中的新成员是由变异而非交叉得来的概率

elite:被认为是最优解且被允许传到下一代的部分

maxtrix:运行运行几代

sol=optimization.geneticoptimize(domain,optimization.schedulecost)
optimization.schedulecost(sol)
#将航班安排表打印
#sol=[1,4,3,2,7,3,6,3,2,4,5,3]
optimization.printschedule(sol)

真实的航班搜索

Kayak API

获取Kayak的xml接口

minidom包

将xml文档当做对象树来看待的标准方式。这个包接受xml的开放文件作为输入,然后返回一个对象,可以利用该对象轻松地提取信息。

航班搜索

新建一个名为kayak.py的文件:
编写代码利用开发者密匙来获得一个新的kayak会话,对xml文件进行解析,以得到sid标签的内容。

import time
import urllib2
import xml.dom.minidom
kayakkey='YOURKEYHERE'
def getkayaksession():
    url='http://www.kayak.com/k/ident/apisession?token%s&version=1'%kayakkey
    doc=xml.dom.minidom.parseString(urllib2.urlopen(url).read())
    sid=doc.getElementsByTagName('sid')[0].firstChild.data
    return sid

学生宿舍优化问题

网络可视化

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!