用迭代局部搜索求解TSP问题(python)

news2024/12/25 9:20:54

文章目录

    • 1.迭代局部搜索(ILS)
    • 2.用ILS解决TSP问题
      • 2.1 函数模块
      • 2.2 主函数
      • 2.3 berlin52数据集测试

1.迭代局部搜索(ILS)

关于迭代局部搜索(ILS iterated local search)的框架和应用总结可以阅读文献Iterated Local Search: Framework and Applications
ILS的核心思想:找到一个局部最优解后,对当前解增加一个扰动,得到新解,从新解基础上再进行局部搜索找到新的局部最优。通过迭代不停的找到新的局部最优比较从而找到全局最优,避免的局部搜索容易陷入局部最优的缺点。
个
算法步骤如下:

  • 生成初始解s(0)
  • 对s0进行局部搜索得到一个局部最优解s*
  • 循环以下过程直到迭代结束条件满足
  • 对当前局部最优解s*进行扰动生成新解s’
  • 对新解s’尽心局部搜索得到新的局部最优解s*’
  • 判断是否接受新的局部最优解s*’

实现迭代局部搜索只需要写四个函数,生成初始解GenerateInitialSolution,局部搜索LocalSearch,扰动函数Perturbation,判断接收新解AcceptanceCriterion。这四个函数的设计方法都会影响最后的搜索解,不同的问题要针对性设计
在这里插入图片描述

2.用ILS解决TSP问题

2.1 函数模块

  • 局部搜索方法

目前求解TSP的局部搜索方法比较好的是k-opt方法,在本人博客Discrete Optimization课程笔记(3)—局部搜索中的Case5:Traveling Salesman Problem(k-opt)有具体描述,此处代码使用2-opt

import numpy as np
import math


class Local2opt(object):
    def __init__(self, points, init):
        self.points = points
        self.num_point = len(points)
        self.init_path = init

        self.best_path = []
        self.best_dist = 0

    def update(self, path, dist):
        self.best_path = path
        self.best_dist = dist
        return self.best_path, self.best_dist

    def two_opt(self, improvement_threshold=0.001):
        self.best_path = self.init_path
        self.best_dist = self.calculate_dist(self.best_path)
        improvement_factor = 1

        while improvement_factor > improvement_threshold:
            pre_best = self.best_dist
            for one in range(1, self.num_point - 2):
                for two in range(one + 1, self.num_point - 1):
                    one_first_point = self.points[self.best_path[one - 1]]
                    one_end_point = self.points[self.best_path[one]]
                    two_first_point = self.points[self.best_path[two]]
                    two_end_point = self.points[self.best_path[two + 1]]
                    before = self.length(one_first_point, one_end_point) + self.length(two_first_point, two_end_point)
                    after = self.length(one_first_point, two_first_point) + self.length(one_end_point, two_end_point)
                    if after < before:
                        new_path = self.swap(self.best_path, one, two)
                        new_dist = self.best_dist + after - before
                        self.update(new_path, new_dist)
            improvement_factor = 1 - self.best_dist / pre_best
        return self.best_path, self.best_dist

    def calculate_dist(self, path):
        path_dist = 0
        for i in range(len(path) - 1):
            point1 = self.points[path[i]]
            point2 = self.points[path[i+1]]
            path_dist += self.length(point1, point2)
        path_dist += self.length(self.points[path[-1]], self.points[path[0]])
        return path_dist

    def valid(self, path):
        return (len(set(path)) == self.num_point) and (sorted(path) == list(range(self.num_point)))

    @staticmethod
    def swap(path, one, two):
        a = np.concatenate((path[0:one], path[two:-len(path) + one - 1:-1], path[two + 1:])).astype(int)
        return a.tolist()

    @staticmethod
    def length(point1, point2):
        return math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2)
  • 扰动函数

用double-bridge move的方法,即将序列分成四段重组
在这里插入图片描述

def perturb(path):
    index = len(path) // 4
    part_one = 1 + random.randint(0, index)
    part_two = part_one + 1 + random.randint(0, index)
    part_three = part_two + 1 + random.randint(0, index)
    return path[:part_one] + path[part_three:] + path[part_two:part_three] + path[part_one:part_two]
  • 判断接收新解

此处设置如果值更优,则接受新解,当然还有很多其它接受标准

  • 生成初始解:随机生成
def generate_initial_solution(node_count):
    return np.random.permutation(node_count)

2.2 主函数

确定好各个函数模块后,按照前面的算法步骤实现迭代局部搜索,max_search为最大迭代次数

def iterated_local_search(node_count, points, max_search):
    init_path = generate_initial_solution(node_count)
    opt = Local2opt(points, init_path)
    best_path, best_dist = opt.two_opt()
    for i in range(max_search):
        current_path = perturb(best_path)
        opt = Local2opt(points, current_path)
        new_path, new_dist = opt.two_opt()
        if new_dist < best_dist:
            best_path = new_path
            best_dist = new_dist
        if i % 50 == 0:
            print(f'number {i}, best:{best_dist}, new:{new_dist}')
    return best_path, best_dist

2.3 berlin52数据集测试

首先读取数据,获取每个点的坐标,用namedtuple方式存储更方便

from collections import namedtuple

Point = namedtuple('Point', ['index', 'x', 'y'])

def read(path):
    with open(path, 'r') as file:
        data = file.read()
    lines = data.split('\n')
    node_count = int(lines[0])
    points = []
    for i in range(1, node_count + 1):
        line = lines[i]
        part = line.split()
        points.append(Point(int(part[0]), float(part[1]), float(part[2])))
    return node_count, points

运行主函数,获取迭代结果,绘制图像

def plot(path, points):
    for i in range(len(path)-1):
        node1 = path[i]
        node2 = path[i + 1]
        plt.plot([points[node1].x, points[node2].x], [points[node1].y, points[node2].y], c='r')
        plt.scatter(points[node1].x, points[node1].y, c='black')
    plt.plot([points[path[-1]].x, points[path[0]].x], [points[path[-1]].y, points[path[0]].y], c='r')
    plt.scatter(points[path[-1]].x, points[path[-1]].y, c='black')
    plt.show()
node_count, points = read('./berlin52.txt')
path, dist = iterated_local_search(node_count, points, 700)
plot(p, points)

迭代结果如下:berlin52求解器最优解为7542,此处最优解为7544,几乎最优

number 0, best:8619.660498923475, new:8619.660498923475
number 50, best:7778.703948691556, new:7778.703948691556
number 100, best:7778.703948691556, new:8287.326075493342
number 150, best:7721.773954616386, new:8401.535472141499
number 200, best:7588.382511657728, new:7957.109605398081
number 250, best:7580.155850970664, new:8034.869737864365
number 300, best:7580.155850970664, new:8224.925876727228
number 350, best:7580.155850970664, new:8108.147967255095
number 400, best:7580.155850970664, new:8227.789379075848
number 450, best:7580.155850970664, new:8206.208800876693
number 500, best:7580.155850970664, new:8014.874253178536
number 550, best:7566.832820859449, new:8496.26795729865
number 600, best:7544.365901904087, new:8390.292579324512
number 650, best:7544.365901904087, new:8113.830524951099
7544.365901904087 [38, 35, 34, 33, 43, 45, 15, 28, 49, 19, 22, 29, 1, 6, 41, 20, 16, 2, 17, 30, 21, 0, 48, 31, 44, 18, 40, 7, 8, 9, 42, 32, 50, 10, 51, 13, 12, 46, 25, 26, 27, 11, 24, 3, 5, 14, 4, 23, 47, 37, 36, 39]

在这里插入图片描述
事实上,由于生成初始解时没有固定随机种子,因此每次运行结果都不同,有好有坏,后续可以改进四个重要的函数模块,让迭代过程更稳定。完整数据和代码见本人github
local-search-for-TSP

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/154810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Kubernetes 企业项目实战】02、基于 Prometheus 和 K8s 构建智能化监控告警系统(下)

目录 一、可视化 UI 界面 Grafana 的安装和配置 1.1 Grafana 介绍 1.2 安装 Grafana 1.3 Grafana 界面接入 Prometheus数据源 1&#xff09;登录 grafana 2&#xff09;配置 grafana 界面 3&#xff09;导入的监控模板 二、kube-state-metrics 组件 2.1 kube-state-m…

JS的基础语法

作者&#xff1a;~小明学编程 文章专栏&#xff1a;JavaEE 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 JavaScript的书写形式 行内式 内嵌式 外部式 注释 输入输出 输入 输出 JS的语法规则 变量 数据类型 数字类型 字符串类型 布尔类型 运算…

YED-M724嵌入式4G Cat1 核心板介绍

YED-M724嵌入式4G Cat1 核心板简介YEDM724 核心板是由银尔达&#xff08;yinerda&#xff09;基于合宙Air724 模组推出的低功耗&#xff0c;超小体积&#xff0c;高性能嵌入式4G Cat1 核心板。YED-M724嵌入式4G Cat1 核心板特点1、支持5-16V 供电&#xff1b;2、工作环境为-35℃…

基于轻量级YOLOV5融合RepVGG的电梯内电动车检测识别分析系统

RepVGG: Making VGG-style ConvNets Great Again 是2021 CVPR的一篇论文&#xff0c;正如他的名字一样&#xff0c;使用structural re-parameterization的方式让类VGG的架构重新获得了最好的性能和更快的速度。将RepVGG的设计思想融合进入到yolov5目标检测模型中是否有性能的提…

多线程案例-实现定时器

1.定时器是什么定时器是软件开发中的一个重要组件,功能是当达到一个特定的时间后,就执行某个指定好的代码定时器是一个非常常用的组件,特别是在网络编程中,当出现了"连接不上,卡了"的情况,就使用定时器做一些操作来止损标准库中也提供了定时器标准库中的Timer类标准库…

阿里妈妈Dolphin智能计算引擎基于Flink+Hologres实践

作者&#xff1a;徐闻春&#xff08;花名 陌奈&#xff09; 阿里妈妈事业部技术专家 本文整理至FlinkHologres实时数仓Workshop北京站&#xff0c;点击查看视频回放>>> 阿里妈妈数据引擎团队负责广告营销计算引擎Dophin的开发&#xff0c;目前支撑百万级广告主的营销…

ios打包证书申请流程

目前的APP开发&#xff0c;多端开发成为了主流&#xff0c;所以开发APP很多都是使用uniapp来开发&#xff0c;而且都是使用windows电脑来开发。但是在打包ios应用的时候&#xff0c;是需要一个p12格式的打包证书和profile描述文件的。 那么这两个文件如何申请呢&#xff1f;这…

关系抽取Casrel实现(Pytorch版)

前言 关系抽取是自然语言处理中的一个基本任务。关系抽取通常用三元组(subject, relation, object)表示。但在关系抽取中往往会面临的关系三元组重叠问题。《A Novel Cascade Binary Tagging Framework for Relational Triple Extraction》提出的CASREL模型可以有效的处理重叠关…

前端基础(十)_Dom自定义属性(带案例)

Dom自定义属性 1.1、为什么要用自定义属性 例&#xff1a;很多个 li 点击变颜色。 <ul><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8&…

LeetCode[373]查找和最小的K对数字

难度&#xff1a;中等题目&#xff1a;给定两个以 升序排列 的整数数组 nums1和 nums2, 以及一个整数 k。定义一对值 (u,v)&#xff0c;其中第一个元素来自 nums1&#xff0c;第二个元素来自 nums2。请找到和最小的 k个数对 (u1,v1), (u2,v2)... (uk,vk)。示例 1:输入: nums1 …

离散数学与组合数学-01集合论

文章目录1.离散数学与组合数学-01集合论1.1 集合定义1.1.1 什么是集合1.1.2 集合案例1.1.3 集合的符号表示1.2 集合表示1.2.1属于关系1.2.2 枚举法1.2.3 叙述法1.2.4 文氏图1.3 集合基数1.3.1 什么是集合基数1.3.2 集合基数案例1.4 集合间关系1.4.1 空集1.4.2 全集1.4.3 集合的…

【Vue + Koa 前后端分离项目实战9】使用开源框架==>快速搭建后台管理系统 -- part9 项目总结

人要保持忙碌&#xff0c;因为忙碌是世界上最便宜的药。 本博客教学视频来源于imoom 《0到1快速构建自己的后台管理系统》课程 官方演示地址&#xff1a;https://talelin.com/ 目录 一、项目介绍 1.技术准备 2.学到的内容 &#xff08;1&#xff09;后端接口实现的业务逻辑…

【Kotlin】标准库函数 ③ ( with 标准库函数 | also 标准库函数 )

文章目录一、with 标准库函数二、also 标准库函数Kotlin 语言中 , 在 Standard.kt 源码中 , 为所有类型定义了一批标准库函数 , 所有的 Kotlin 类型都可以调用这些函数 ; 一、with 标准库函数 with 函数 与 run 函数 功能是一样的 , 其使用形式不同 , with 函数是 独立使用的 …

LaoCat带你认识容器与镜像(三【下】)

说随缘就随缘&#xff0c;前天刚说惰怠今天就更新~。 本章内容 Docker端口映射相关。 本文实操全部基于Ubuntu 20.04 宿主机 > linux服务器本身 二章二小节中介绍docker create命令时就提供了常用的附加参数列表&#xff0c;docker run可附加参数同create命令附加参数一致&…

Flink【0】初识Flink快速又灵巧

文章目录一、Flink的引入二、Flink的起源和设计理念1.起源2.设计流程一、Flink的引入 随着大数据的飞速发展&#xff0c;出现了很多热门的开源社区&#xff0c;其中著名的有 Hadoop、Storm&#xff0c; 以及后来的Spark&#xff0c;他们都有着各自专注的应用场景。尤以Spark掀…

c++11 标准模板(STL)(std::forward_list)(四)

定义于头文件 <forward_list> template< class T, class Allocator std::allocator<T> > class forward_list;(1)(C11 起)namespace pmr { template <class T> using forward_list std::forward_list<T, std::pmr::polymorphic_…

软件测试:缺陷管理制度

缺陷管理制度 编制部门&#xff1a; 时间&#xff1a;编 制 人&#xff1a; 时间&#xff1a;标 准 化&#xff1a; 时间&#xff1a;审 核&#xff1a; 时间&#xff1a;批 准&#xff1a; …

(一)STM32L4(RT- Thread)——电机和蜂鸣器,独立按键,LED灯

&#xff08;一&#xff09;STM32L4&#xff08;RT- Thread&#xff09;——电机和蜂鸣器&#xff0c;独立按键&#xff0c;LED灯 文章目录&#xff08;一&#xff09;STM32L4&#xff08;RT- Thread&#xff09;——电机和蜂鸣器&#xff0c;独立按键&#xff0c;LED灯LED灯学…

5.8.2、TCP 的连接释放

1、释放流程图 释放流程图 2、TCP 使用 “四报文挥手” 释放连接的具体过程 TCP 通过 “四报文挥手” 来释放连接 数据传输结束后&#xff0c;TCP 通信双方都可以释放连接 现在 TCP 客户进程和 TCP 服务器进程都处于连接已建立状态\color{blue}连接已建立状态连接已建立状态…

k8s 文件 目录挂载

k8s 文件 目录挂载1.环境说明2.HostPath 挂载至宿主机3.挂载至nfs3.1 第一种3.2 第二种3.3 volumeClaimTemplates 挂载模板4.emptyDir-临时数据卷5.未完待续1.环境说明 k8s 1.24.3 2.HostPath 挂载至宿主机 apiVersion: v1 kind: Pod metadata:name: test-pd spec:container…