机器学学---航班优化问题

简介:
# -*- coding: UTF-8 -*-
# 对组团旅游计划进行优化
# 对来自不同地方的人去往同一地点的人们安排一次合理的旅游计划

import time
import random
import math 

    
people = [('Seymour','BOS'),('Franny','DAL'),('Zooey','CAK'),('Wait','MIA'),('Buddy','ORD'),('Les','OMA')]

# Glass 一家6口人从五湖四海赶来,都要在同一天到达目的地 LGA机场,并且在同一天离开,他们想搭乘相同的交通工具往返机场
destination = 'LGA' # Newyork LGA 机场

flights = {}

r = open('C:/Users/jt90787/Desktop/AIdatafile/ariplane.txt','r')
for line in r.readlines():
    origin,dest,depart,arrive,price = line.strip().split(',')
    
    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]    
  
#最优解,不仅仅是要让总票价将下来,还要考虑到飞行时间和候机时间
 
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])
        

# 确定成本函数,在这个案例中需要考虑的变量, 价格,旅行时间,机场等待时间,出发时间,汽车租用时间。
# 通过飞行策略来计算旅行总cost
def schedulecost(sol):
    totalprice = 0
    latestarrival = 0
    earliestdep = 24*60
    
    for d in range(len(sol)/2):
        origin = people[d][1]
        #往返航班
        outbound = flights[origin,destination][int(sol[2*d])]
        returnf = flights[destination,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,destination][int(sol[2*d])]
        returnf = flights[destination,origin][int(sol[2*d+1])]
        
        #所有人等待时间总和,等待时间换算是一分钟一美元
        totalwait+=latestarrival-getminutes(outbound[1])
        totalwait+=getminutes(returnf[0]) - earliestdep
    
    # 租车要用50美元    
    if latestarrival<earliestdep: totalprice+=50
    
    return totalprice+totalwait
    
# 随机搜索,用来评估其他算法优劣的基线,也可以让我们更清楚地理解所有算法的真正意图
def randomoptimize(domain,costf):
    best = 999999999
    bestr = None
    for j in range(10000):
        #创建一个随机解
        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
    
#     print best
    return bestr

# 随即算法是蛮干,毫无依据的进行尝试,其实我们可以通过已经的得出的优解进行调整测试
#爬山法,在其邻近解中寻求更优解,对于最初的一个随即安排我们可以对某个人使用其相邻航班去计算成本    
# 缺点。得出的是局部最优解,它只是一个初始随机解的相邻解中的最优解,就像极小值一样,不是全局最优解,不是最小值 
def hillclimb(domain,costf,sol):
    # 随机一个解
    if sol == None:
        sol = [random.randint(item[0], item[1]) for item in domain]

    best_cost = 99999
    while True:
        # 记录周围解
        neigbors = []
        # 对于所有人的解,都进行修改
        for i in range(len(domain)):
            if sol[i] > domain[i][0]:
                neigbors.append(sol[0:i] + [sol[i] - 1] + sol[i + 1:])
            if sol[i] < domain[i][1]:
                neigbors.append(sol[0:i] + [sol[i] + 1] + sol[i + 1:])

        for item in neigbors:
            cost = costf(item)
            if cost < best_cost:
                best_cost = cost
                sol = item
        # 最优解是上次的解,说明已经到了最低点
        if sol not in neigbors:
            return best_cost, sol
        
# 退火法
# T代表温度,cool代表退火率,step代表改变范围
def backfire(domain, fn_cost=schedulecost, T=10000, cool=0.95, step=3):
    # 随机解
    sol = [random.randint(item[0], item[1]) for item in domain]

    while T > 0.1:
        # 随机出来一个要改变的位置
        i = random.randint(0, len(domain) - 1)
        # 随机一个要改变的数
        change = random.randint(-step, step)

        sol_copy = sol[:]
        sol_copy[i] += change
        # 保证数据没有越界
        if sol_copy[i] < domain[i][0]:
            sol_copy[i] = domain[i][0]

        if sol_copy[i] > domain[i][1]:
            sol_copy[i] = domain[i][1]
        c = fn_cost(sol)
        cc = fn_cost(sol_copy)
        # 如果是更优解或者现在的随机概率在范围内
        # 随着计算的进行,此范围会越来越小,越来越倾向于接收更优解
        if cc < c or random.random() < pow(math.e, -(cc - c) / T):
            sol = sol_copy[:]

        T *= cool

    return fn_cost(sol), sol

# 遗传算法的运行过程是先随机生成一组解所谓初始族群,族群中每次迭代都会选取更优秀的一部分,
# 然后这部分优秀种进行变异或者交叉来补充族群到最大,并重复这一过程
# 变异示例:
# ​ [7,5,3,2,5,3,0,1,1,5,3,6] ->[7,5,3,2,5,3,5,1,1,5,3,6]
# ​ 交叉示例:
# ​ [7,5,3,2,5,3,**交叉位置**0,1,1,5,3,6]
# ​ [3,8,2,6,5,4,**交叉位置**1,2,3,5,4,6]
# ​ ->[7,5,3,2,5,3,1,2,3,5,4,6]
    # popsize:数据量  variation:变异概率, elite:遗传率,  maxiter:遗传多少代
def genetic(domain,fn_cost=schedulecost,popsize=50,variation=0.2,elite=0.3,maxiter=100):
    # 变异
    def mutate(r):
        i = random.randint(0, len(domain) - 1)
        # 两个方向的变异概率都是50%,random.random() 随机数范围小于一
        if random.random() < 0.5:
            res = r[0:i] + [r[i] - 1] + r[i + 1:]
        else:
            res = r[0:i] + [r[i] + 1] + r[i + 1:]

        if res[i] < domain[i][0]:
            res[i] = domain[i][0]

        if res[i] > domain[i][1]:
            res[i] = domain[i][1]
        return res

    # 交叉
    def crossover(r1, r2, pops):
        if r1 is None or r2 is None:
            for line in pops:
                print(str(line))
        # 交叉必然发生
        i = random.randint(1, len(domain) - 2)
        return r1[0:i] + r2[i:]
    
    # 计算能存活下来的数量。每一代可以存活下来的个体数量
    elite_count = int(popsize * elite)
    
    #随机出第一代,一共50个个体
    pops = [[random.randint(item[0],item[1]) for item in domain] for i in range(popsize)]
    
    # 一共遗传100代
    for i in range(maxiter):
        # 计算消耗,每一个个体的消耗
        pops_cost = [(fn_cost(item), item) for item in pops]
        pops_cost.sort()
       
        # 只取最优解遗传,保留下比较优秀的前elite_count 个
        pops = [item[1] for item in pops_cost[0:elite_count]]
        
        # 补充族群至最大
        while len(pops) < popsize:
            # 随机进行变异和交叉
            # 变异 ,变异率 0.2
            if random.random() < variation:
                # 对族群中的随机一个个体进行变异
                pops.append(mutate(pops[random.randint(0, len(pops) - 1)]))
            else:
                # 随机选出两个解进行交叉,类似于繁衍,两个个体诞生一个新个体,父本母本各取一些基因
                r1 = pops[random.randint(0, len(pops) - 1)]
                r2 = pops[random.randint(0, len(pops) - 1)]
                pops.append(crossover(r1, r2, pops))
                
    pops_cost = [(fn_cost(item), item) for item in pops]
    pops_cost.sort()
    return pops_cost[0]


if __name__ == '__main__':
    # 分别代表每一个家庭成员的飞行策略,最终就是要确定这样一个飞行策略 
    # 1,4 代表Seymour 从BOS到LGA做这一天的第2次航班, 从LGA回到BOS座第5趟航班
    s = [1,3,3,2,7,3,6,3,2,4,5,3]
    
    print flights
    print schedulecost(s)
    
    #各位置取值范围,初始化飞行策略,每个人的航班每天都有10趟
    domain = [(0,9)]*(len(people)*2)
    
    #随机搜索
    r = randomoptimize(domain, schedulecost)
    print '----------------------------------随机搜索-----------------------------------------------'
    print schedulecost(r),r
    printschedule(r)

    #爬山法
    b = hillclimb(domain, schedulecost,None)
    print '--------------------------------爬山法-----------------------------------------------'
    print b
    printschedule(b[1])

    #爬山法
    b = hillclimb(domain, schedulecost,r)
    print '--------------------------------利用随机搜索之后的最优解再使用--爬山法-----------------------------------------------'
    print b
    printschedule(b[1])
    
    #退火算法
    print '--------------------------------退火算法-----------------------------------------------'
    fire = backfire(domain,schedulecost)
    print fire
    printschedule(fire[1])
    
    #遗传算法
    print '--------------------------------遗传算法-----------------------------------------------'
    gene = genetic(domain,schedulecost)
    print gene
    printschedule(gene[1])
相关文章
|
8月前
|
机器学习/深度学习 人工智能 开发框架
机器博弈 (二) 遗憾最小化算法
机器博弈 (二) 遗憾最小化算法
|
10月前
|
C++
信奥赛一本通1122:计算鞍点
【题目描述】 给定一个5×5的矩阵,每行只有一个最大值,每列只有一个最小值,寻找这个矩阵的鞍点。鞍点指的是矩阵中的一个元素,它是所在行的最大值,并且是所在列的最小值。 例如:在下面的例子中(第4行第1列的元素就是鞍点,值为8 )。 11 3 5 6 9 12 4 7 8 10 10 5 6 9 11 8 6 4 7 2 15 10 11 20 25
413 0
(拯救选择困难症)随机选择今天中午吃啥
(拯救选择困难症)随机选择今天中午吃啥
(拯救选择困难症)随机选择今天中午吃啥
|
达摩院 算法 API
如何吃,少花钱又营养丰富?可用MindOpt线性规划求解来决策
营养调配问题的的目标是利用优化模型来设定每日饮食菜单,在满足各类营养的需求同时更能优化总成本
如何吃,少花钱又营养丰富?可用MindOpt线性规划求解来决策
L2-040 哲哲打游戏 (25 分)(模拟)
L2-040 哲哲打游戏 (25 分)(模拟)
100 0
|
JavaScript
小明特别喜欢打扑克牌,除了喜欢斗地主和德州扑克之外,还喜欢一种叫桥牌的游戏,桥牌的具体规则相当复杂,有叫牌、打牌和计分三个阶段,还有不断变化的局况,局况可能影响叫牌打牌策略。但是小明暂时不关心这一些,
小明特别喜欢打扑克牌,除了喜欢斗地主和德州扑克之外,还喜欢一种叫桥牌的游戏,桥牌的具体规则相当复杂,有叫牌、打牌和计分三个阶段,还有不断变化的局况,局况可能影响叫牌打牌策略。但是小明暂时不关心这一些,
284 0
小明特别喜欢打扑克牌,除了喜欢斗地主和德州扑克之外,还喜欢一种叫桥牌的游戏,桥牌的具体规则相当复杂,有叫牌、打牌和计分三个阶段,还有不断变化的局况,局况可能影响叫牌打牌策略。但是小明暂时不关心这一些,
算法每日一题——第七天——消除游戏
算法每日一题——第七天——消除游戏
算法每日一题——第七天——消除游戏
|
机器学习/深度学习 算法 机器人
高数有救了!神经网络不到一秒就能求解偏微分方程,也是工程物理界的福音
对于特别复杂的偏微分方程,可能需要数百万个CPU小时才能求解出来一个结果。随着问题越来越复杂,从设计更优秀的火箭发动机到模拟气候变化,科学家们需要一个更「聪明」的求解方法。
258 0
高数有救了!神经网络不到一秒就能求解偏微分方程,也是工程物理界的福音
|
机器学习/深度学习 人工智能 自然语言处理
用魔法打败魔法!用狗屁不通文章生成器写高三作文,评分软件给分84.4,打败73.5%学生
用魔法打败魔法!用狗屁不通文章生成器写高三作文,评分软件给分84.4,打败73.5%学生
284 0