写给媳妇儿的算法(二)——2-opt算法解决商旅问题

【引言】一个旅行商,想要从A城市出发,途径BCDEFGH城市,最终返回A城市。每个城市之间的距离可能都是不一样的,那么他该以一个什么样的顺序,每个城市都经过一次的情况下使得他所走的路程最短呢?

商旅问题是一个如果按照正经的方法解决,算法的效率会随着数据的增加爆炸的问题。

算法过程

试想旅行商如果在出发前看一遍所有的路线方案,那么路线方案的数量会随着途径城市的增多而出现爆炸性增长的情况:

路线随着途径城市的增多而增加.png

这种情况下,算法的复杂度会是O(n!), O(n!)是个什么概念呢?:
时间复杂度的图像.png

所以,如果在出发前查看所有的路线方案,如果途径城市很多,会是个非常爆炸的计算数量。

2-opt算法

2-opt算法的核心在于随机选择一个区间段进行优化,这个优化只是对于当前一个状态的优化,并不是对全局的优化。
算法的步骤:
首先确定算法的最大迭代次数MAX,初始化一个计数器N用于记录迭代次数

  • 1、随机一条初始化可选择的路线,途径所有城市,比如: A => B => C => D => E => F => G = > H => A, 假设这一条就是最短的路径。

  • 2、 随机选择两个不同的途径的城市,反转这两个城市在内的中间的路线,比如随机选择(C、F)
    那么此时 老路径 被分割成了三段:
    (A => B) =>( C => D => E => F )=> (G = > H => A)
    翻转后,得到的 新路径
    (A => B) =>( F => E => D => C )=> (G = > H => A)

  • 3、如果新路径(A => B => F => E => D => C => G = > H => A)的距离总长 小于 老路径(A => B => C => D => E => F => G = > H => A)距离总长,那么最短的路径变为新路径,计数器N=0;如果新路径距离总长 大于 老路径,那么老路径还是当前的最短路径,计数器N+1。如果 N ≥ MAX , 算法结束,当前的路径就是最短路径(局部最优的最短路径)。
    这个算法得到的路线是局部最优的,也就是它会根据迭代次数的不一样,可能呈现出不一样结果,并不是绝对正确的结果,只是优化后的相对正确。如果要得到绝对正确的结果,就需要把所有的路线都列出来计算所有的距离,这样就会爆炸。

算法实现

运气好的话,就能用这个数据源能跑出来一颗心,送给媳妇儿:

#coding: utf-8

import numpy as np 
import matplotlib.pyplot as plt 
import numpy.random as random 

# 600*600的
cities = np.array([[300,0],
                  [400, 50],
                  [450, 100],
                  [500, 200],
                  [550, 300],
                  [600, 400],
                  [500, 500],
                  [400, 500],
                  [300, 400],
                  [200, 500],
                  [100, 500],
                  [0, 400],
                  [50, 300],
                  [100, 200],
                  [150, 100],
                  [200, 50]])

# 2-opt算法 #                 
COUNT_MAX = 520

# 因为自己造的输入数据可能是最佳路径,所以先获取一个随机的路线(任选一个可行解)
def get_random_path(best_path):
    random.shuffle(best_path)
    path = np.append(best_path, best_path[0])
    return path

# 计算两个点的距离
def calculate_distance(from_index, to_index):
    return np.sqrt(np.sum(np.power(cities[from_index] - cities[to_index], 2)))

# 计算整条路径的距离
def calculate_path_distance(path):
    sum = 0.0
    for i in range(1, len(path)):
        sum += calculate_distance(path[i-1], path[i])
    return sum

# 获取随机的起始点还有中间的反转后的路径
def get_reverse_path(path):
    start = random.randint(1, len(path) - 1)
    while True:
        end = random.randint(1, len(path) - 1)
        if np.abs(start - end) > 1:
            break
    
    if start > end:
        path[end: start+1] = path[end: start+1][::-1]
        return path
    else:
        path[start: end+1] = path[start: end+1][::-1]
        return path

# 比较两个路径的长短
def compare_paths(path_one, path_two):
    return calculate_path_distance(path_one) > calculate_path_distance(path_two)

# 不断优化,得到一个最终的最短的路径
def update_path(path):
    count = 0
    while count < COUNT_MAX:
        reverse_path = get_reverse_path(path.copy())
        if compare_paths(path, reverse_path):
            count = 0
            path = reverse_path
        else:
            count += 1
    return path

def opt_2():
    best_path = np.arange(len(cities))
    best_path = get_random_path(best_path)
    path = update_path(best_path)
    show(path)

def show(path):
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(cities[:, 0], cities[:, 1], 'o', color='red')
    ax.plot(cities[path, 0], cities[path, 1], color='red')
    plt.show()

opt_2()

送给媳妇儿.png



上一篇:写给媳妇儿的算法(一)——二分查找
下一篇:写给媳妇儿的算法(三)——选择排序

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • 解法一 最容易想到的方法是先对元素进行排序,然后取出前k个数,总时间复杂度O(n*logN)。你一定注意到了,当k...
    书呆子的复仇阅读 3,816评论 2 1
  • 1、在靠谱这件事儿上你觉得你做得如何?举例说明。 1)靠谱是基础,也就是孔子说的“人而无信不知其可也”。失信不立。...
    牛文华阅读 185评论 0 0
  • 这几天冯唐的文章刷遍朋友圈,那我就比较厉害了,我是素食主义,常年吃素。所以不油腻。 遇到【简书】有...
    拓江笔记阅读 449评论 0 0
  • 曾经的我们,向往长大,向往社会,向往爱情,向往一切美好的东西。可是,渐渐长大,发现当初说好一起走下去的伙伴们,早已...
    楽楽33333阅读 197评论 0 1