Tabu Search — 温和介绍

news2024/11/28 10:40:00

Tabu Search — 温和介绍

目录

Tabu Search — 温和介绍

一、说明

二、什么是禁忌搜索以及我可以在哪里使用它?

三、禁忌搜索原则

四、短期记忆和积极搜索:

五、举例时间 

六、结论:

七、参考:


一、说明

最近,我参加了 Corsera 上的离散优化课程。我试图解决的问题之一是旅行商问题,即著名的 NP-Hard 优化问题。该课程讲解了如何使用几种算法解决几个实际问题,其中一种算法是禁忌搜索。

今天,在这篇文章中,我将解释该算法并使用 Python 实现它来解决旅行商问题 TSP。

二、什么是禁忌搜索以及我可以在哪里使用它?

禁忌搜索是一种用于解决优化问题的元启发式程序,旨在指导其他方法摆脱局部最小值的陷阱。禁忌搜索用于为各种经典和实际问题寻找最佳和接近最佳的解决方案。从调度到电信,从字符识别到神经网络。为了避免陷入局部最小值,它使用内存,以便能够记住已经利用的动作和解决方案。此外,它还使用记忆功能来允许搜索策略,例如强化和多样化(稍后将对其进行解释)。

禁忌搜索可用于指导其他过程,该过程使用一组动作将一个解决方案转换为另一个解决方案,并为衡量这些动作的吸引力提供指导。动作的示例是在两个任务之间交换,改变变量的值(增加、减少)。

Tabu Search 应用的部分列表:

  1. 员工排班
  2. 最大可满足性问题
  3. 字符识别
  4. 空间规划与建筑设计
  5. 电信路径分配
  6. 概率逻辑问题
  7. 神经网络模式识别
  8. 机器调度
  9. 旅行商问题
  10. 图形着色

三、禁忌搜索原则

Tabu Search基于三个主要概念:

  1. 使用基于灵活属性的记忆结构,可以比严格的记忆结构或无记忆系统更彻底地利用评估标准和历史搜索信息。
  2. 禁忌搜索中根据特定标准评估移动的机制
  3. 使用从短期到长期的不同时间跨度的记忆功能来实施强化策略——专注于特定区域,再到多样化——推动在新区域的搜索。

四、短期记忆和积极搜索:

        禁忌搜索中的短期记忆可以表示为一种积极的利用,旨在使基本移动成为可能。通过列出可以从当前解决方案中得出新解决方案的候选移动列表。

        禁忌搜索提供了防止重复某些动作的限制。通过将这些动作视为禁忌(禁忌)……(啊,这就是它被称为禁忌搜索的原因!)。禁忌搜索旨在防止循环回到局部最小值,并广泛地引入搜索以遵循新的轨迹。

        但是.. Tabu 如何确定最佳候选人?很简单!使用目标函数!..事情还是不清楚?!别担心,在看到下面的框图之前,我也不明白:

        禁忌搜索短期记忆组件——取自[1]

选择最佳候选人——取自[1]

五、举例时间 

        ِ禁忌搜索的一个简单例子是最小成本生成树问题,其中包括防止某些边出现的约束。该问题可以用五个节点表示,因此生成树由四个边组成,每个边都有一个成本,如下图所示:

生成树问题

在这个问题中,我们需要最小化节点相互连接的成本。这里每条边都用 xi 表示,其中 i=[0..7],xi 可以取 0 或 1(0 表示边不存在)。每条边都有成本。例如,这里初始解决方案的成本是 6+2+8+0 = 16(相当不错吧)。

但是等等..不要忘记问题的另一部分,有些边是禁止出现..所以让我们在这里加上一些限制:

x1 + x2 + x6≤1

x1≤x3

当我们违反这些限制时,我们会对每个违反单位处以 50 的惩罚。因此,在上图中,我们有 2 次违反,因此惩罚为 100。该生成树的总成本为116 :)

我们需要找到一种成本低且不违反上述约束的解决方案。要应用禁忌搜索,我们需要应用交换变换,即删除一条边并添加另一条边以转换为另一个解决方案。

当我们选择添加一条边时,我们必须以不产生循环的方式删除另一条边。可接受的移动是考虑到违规惩罚成本而具有最低成本的移动。

此处的禁忌限制定义为我们定义为禁忌状态的添加边。这使得被选为禁忌的边只要是禁忌就不会从树中删除。在示例中,我们只允许两条边是禁忌。这意味着任何添加的边在两次迭代中仍然是禁忌。

此处的期望标准是,如果生成的树比迄今为止生成的最佳树更好,则覆盖禁忌状态。我们现在将运行该算法并讨论每次迭代。迭代如下图 1 所示:

迭代 1:从迭代 1 开始,我们可以采取的最佳措施是添加 x3 并删除 x1,这样既不违反约束,又能达到最佳效果。这会将惩罚从 100 减少到 0。同时将成本从 16 增加到 28。X3 现在被视为禁忌。

迭代 2:现在根据禁忌状态规则,我们将 x3 设为禁忌。因此我们不能删除 x3。从剩余的选项中,最佳选择是添加 x7 并删除 x6。然后将 x7 设为禁忌。所选的举动产生的成本比前一个更糟糕。

现在我们有 x3,x7 被视为禁忌。如果我们想采取任何行动,我们都会使成本变得更糟。所以我们处于一种成本很高的状态,我们无法采取任何其他行动,这被称为局部最小值。

迭代 3:现在,由于我们达到了局部最小值,我们可以通过添加 x2 并删除 x3 来覆盖禁忌状态。此举通过生成具有更佳成本的树来满足期望标准,因此我们采取此举。

迭代 4:X2、X7 现在是禁忌。算法将继续添加 x3 并删除 X5。X3 和 X2 现在是禁忌

执行:

我在 Python 中分叉了一个禁忌搜索的实现,并对其进行了改进,以解决旅行商问题,请随意使用和修改代码:

"""
This implementation for tabu search is modified from:
https://www.techconductor.com/algorithms/python/Search/Tabu_Search.php
Reference:
https://www.researchgate.net/publication/242527226_Tabu_Search_A_Tutorial
"""
import copy
import math


def distance(point1, point2):
    return math.sqrt((point1.x - point2.x)**2 + (point1.y - point2.y)**2)


def generate_neighbours(points):
    """This function geenrates a 2D distance matrix between all points
    Parameters
    ----------
    points : type
        Description of parameter `points`.
    Returns
    -------
    type
        Description of returned object.
    """
    dict_of_neighbours = {}

    for i in range(len(points)):
        for j in range(i+1, len(points)):
            if i not in dict_of_neighbours:
                dict_of_neighbours[i] = {}
                dict_of_neighbours[i][j]= distance(points[i], points[j])
            else:
                dict_of_neighbours[i][j] = distance(points[i], points[j])
                # dict_of_neighbours[i] = sorted(dict_of_neighbours[i].items(), key=lambda kv: kv[1])
            if j not in dict_of_neighbours:
                dict_of_neighbours[j] = {}
                dict_of_neighbours[j][i] = distance(points[j], points[i])
            else:
                dict_of_neighbours[j][i] = distance(points[j], points[i])
                # dict_of_neighbours[i] = sorted(dict_of_neighbours[i].items(), key=lambda kv: kv[1])


    return dict_of_neighbours


def generate_first_solution(nodes, dict_of_neighbours):
    start_node = nodes[0]
    end_node = start_node

    first_solution = []
    distance = 0
    visiting = start_node
    pre_node = None
    while visiting not in first_solution:
        _tmp = copy.deepcopy(dict_of_neighbours[visiting])
        _tmp.pop(pre_node, None)
        next_node = min(_tmp.items(), key=lambda x: x[1])[0]
        distance += dict_of_neighbours[visiting][next_node]
        first_solution.append(visiting)
        pre_node = visiting
        visiting = next_node

    first_solution.append(nodes[0])
    distance += dict_of_neighbours[pre_node][end_node]
    return first_solution, distance

def find_neighborhood(solution, dict_of_neighbours, n_opt=1):
    neighborhood_of_solution = []
    for n in solution[1:-n_opt]:
        idx1 = []
        n_index = solution.index(n)
        for i in range(n_opt):
            idx1.append(n_index+i)

        for kn in solution[1:-n_opt]:
            idx2 = []
            kn_index = solution.index(kn)
            for i in range(n_opt):
                idx2.append(kn_index+i)
            if bool(
                set(solution[idx1[0]:(idx1[-1]+1)]) &
                set(solution[idx2[0]:(idx2[-1]+1)])):
          
                continue
          

            _tmp = copy.deepcopy(solution)
            for i in range(n_opt):
                _tmp[idx1[i]] = solution[idx2[i]]
                _tmp[idx2[i]] = solution[idx1[i]]

            distance = 0
            for k in _tmp[:-1]:
                next_node = _tmp[_tmp.index(k) + 1]
                distance = distance + dict_of_neighbours[k][next_node]
                
            _tmp.append(distance)
            if _tmp not in neighborhood_of_solution:
                neighborhood_of_solution.append(_tmp)

    indexOfLastItemInTheList = len(neighborhood_of_solution[0]) - 1

    neighborhood_of_solution.sort(key=lambda x: x[indexOfLastItemInTheList])
    return neighborhood_of_solution


def tabu_search(first_solution, distance_of_first_solution, dict_of_neighbours, iters, size, n_opt=1):
    count = 1
    solution = first_solution
    tabu_list = list()
    best_cost = distance_of_first_solution
    best_solution_ever = solution
    while count <= iters:
        neighborhood = find_neighborhood(solution, dict_of_neighbours, n_opt=n_opt)
        index_of_best_solution = 0
        best_solution = neighborhood[index_of_best_solution]
        best_cost_index = len(best_solution) - 1
        found = False
        while found is False:
            i = 0
            first_exchange_node, second_exchange_node = [], []
            n_opt_counter = 0
            while i < len(best_solution):
                if best_solution[i] != solution[i]:
                    first_exchange_node.append(best_solution[i])
                    second_exchange_node.append(solution[i])
                    n_opt_counter += 1
                    if n_opt_counter == n_opt:
                        break
                i = i + 1

            exchange = first_exchange_node + second_exchange_node
            if first_exchange_node + second_exchange_node not in tabu_list and second_exchange_node + first_exchange_node not in tabu_list:
                tabu_list.append(exchange)
                found = True
                solution = best_solution[:-1]
                cost = neighborhood[index_of_best_solution][best_cost_index]
                if cost < best_cost:
                    best_cost = cost
                    best_solution_ever = solution
            elif index_of_best_solution < len(neighborhood):
                best_solution = neighborhood[index_of_best_solution]
                index_of_best_solution = index_of_best_solution + 1

        while len(tabu_list) > size:
            tabu_list.pop(0)

        count = count + 1
    best_solution_ever.pop(-1)
    return best_solution_ever, best_cost

六、结论:

禁忌搜索是一种元启发式搜索算法,利用短期记忆的思想来避免陷入局部最小值。它已用于许多应用,其中之一就是旅行商问题。谈到 TSP,值得一提的是,解决它的最佳算法是引导局部搜索算法。

七、参考:

我使用以下参考资料作为本文所写的主要信息来源(这确实是禁忌搜索的最佳资源:

  1. https://www.researchgate.net/publication/242527226_Tabu_Search_A_Tutorial
  2. https://en.wikipedia.org/wiki/Tabu_search
  3. http://www.cleveralgorithms.com/nature-inspired/stochastic/tabu_search.html

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

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

相关文章

《向量数据库指南》——Milvus Cloud检索器增强的深度探讨:句子窗口检索与元数据过滤

检索器增强的深度探讨&#xff1a;句子窗口检索与元数据过滤 在信息爆炸的时代&#xff0c;高效的检索系统成为了连接用户与海量数据的关键桥梁。为了进一步提升检索的准确性和用户满意度&#xff0c;检索器增强技术应运而生&#xff0c;其中句子窗口检索与元数据过滤作为两大…

coco数据集格式计算mAP的python脚本

目录 背景说明COCOeval 计算mAPtxt文件转换为coco json 格式自定义数据集标注 背景说明 在完成YOLOv5模型移植&#xff0c;运行在板端后&#xff0c;通常需要衡量板端运行的mAP。 一般需要两个步骤 步骤一&#xff1a;在板端批量运行得到目标检测结果&#xff0c;可保存为yol…

Django文档简化版——Django快速入门——创建一个基本的投票应用程序(3)

续上一篇&#xff0c;这一篇 着重于创建公共接口——“视图” 第三部分——3、视图和模板 1、概述2、编写更多视图原理——django依次访问了什么文件 3、写一个真正有用的视图一个快捷函数 render() render——渲染 4、抛出404错误一个快捷函数 get_object_or_404() 5、使用模…

【零基础】学JS

喝下这碗鸡汤 “知识就是力量。” - 弗朗西斯培根 1.三元运算符 目标:能利用三元运算符执行满足条件的语句 使用场景:其实是比if双分支更简单的写法&#xff0c;可以使用三元表达式 语法&#xff1a;条件 ? 满足条件的执行代码 : 不满足条件执行的代码 接下来用一个小案例来展…

英语学习交流小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;每日打卡管理&#xff0c;备忘录管理&#xff0c;学习计划管理&#xff0c;学习资源管理&#xff0c;论坛交流 微信端账号功能包括&#xff1a;系统首页&#xff0c;学习资源&…

AI周报(6.30-7.6)

AI应用-AI控制F16战机与人类飞行员狗斗 2024年美国国防部领导下的国防部高级研究计划局&#xff08;DARPA&#xff09;宣布&#xff0c;世界上首次人工智能&#xff08;AI&#xff09;驾驶的战斗机与人类驾驶的战斗机之间的空战&#xff0c;于去年秋季在加利福尼亚州爱德华兹空…

平台稳定性里程碑 | Android 15 Beta 3 已发布

作者 / 产品管理副总裁、Android 开发者 Matthew McCullough 从近期发布的 Beta 3 开始&#xff0c;Android 15 达成了平台稳定性里程碑版本&#xff0c;这意味着开发者 API 和所有面向应用的行为都已是最终版本&#xff0c;您可以查阅它们并将其集成到您的应用中&#xff0c;并…

并口、串口和GPIO口区别

并口 并行接口,简称并口。并口采用的是25针D形接头。所谓“并行”,是指8位数据同时通过并行线进行传送,这样数据传送速度大大提高,但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错,目前,并行接口主要作为打印机端口等。 并口的工作模式 …

【小沐学Python】在线web数据可视化Python库:Bokeh

文章目录 1、简介2、安装3、测试3.1 创建折线图3.2 添加和自定义渲染器3.3 添加图例、文本和批注3.4 自定义您的绘图3.5 矢量化字形属性3.6 合并绘图3.7 显示和导出3.8 提供和筛选数据3.9 使用小部件3.10 嵌入Bokeh图表到Flask应用程序 结语 1、简介 https://bokeh.org/ https…

JVM原理(二十):JVM虚拟机内存的三特性详解

1. 原子性、可进行、有序性 1.1. 原子性 Java内存模型围绕着在并发过程中如何处理原子性、可见性和有序性这三个特征来建立的。 Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write这六个。我们大致可以认为&#xff0c;基本数据类型的访问、…

给csv或txt文件加上一列id

文章目录 前言代码 前言 从这样 变成这样 代码 import pandas as pd for i in range(0,10):data pd.read_csv(/home/yin/DREAMwalk-main/DREAMwalk-main/demo/LR/result/disease_label_herb_drug_{}.txt.format(i),sep\t, header0)n len(data)1nlist range(1,n)data[id] …

Amesim中删除计算结果保存计算文件

前言 Amesim在工程应用中计算的结果文件有时会很大&#xff0c;为了节省电脑存储空间&#xff0c;项目结束后可以将计算结果删除进行保存以存档。 操作步骤 具体操作步骤如下&#xff1a; Step1&#xff1a;在①File下打开&#xff08;Open&#xff09;需要删除计算结果的项…

安卓备忘录App开发

安卓备忘录APP开发,文章末尾有源码和apk安装包 目标用户: 普通安卓手机用户,需要一个简单易用的备忘录App来记录和管理日常事务。 主要功能: 用户注册: 用户可以创建一个账号,输入用户名和密码。 用户登录: 用户可以通过用户名和密码登录到应用。 用户信息存储: 用户名和…

机器学习原理之 -- 神经网络:由来及原理详解

神经网络&#xff08;Neural Networks&#xff09;是受生物神经系统启发而设计的一类计算模型&#xff0c;广泛应用于图像识别、语音识别、自然语言处理等领域。其基本思想是通过模拟人脑神经元的工作方式&#xff0c;实现对复杂数据的自动处理和分类。本文将详细介绍神经网络的…

缓存-缓存使用2

1.缓存击穿、穿透、雪崩 1.缓存穿透 指查询一个一定不存在的数据&#xff0c;由于缓存是不命中&#xff0c;将去查询数据库&#xff0c;但是数据库也无此纪录&#xff0c;我们没有将这次查询的null写入缓存&#xff0c;这将导致这个不存在的数据每次请求都要到存储层去查询&a…

算法 —— 二分查找

目录 二分查找 在排序数组中查找元素的第一个和最后一个位置 搜索插入位置 x的平方根 山峰数组的峰顶索引 寻找峰值 搜索旋转排序数组中的最⼩值 点名 二分查找模板分为三种&#xff1a;1、朴素的二分模板 2、查找左边界的二分模板 3、查找右边界的二分模板&#xf…

scrapy写爬虫

Scrapy是一个用于爬取网站数据并提取结构化信息的Python框架 一、Scrapy介绍 1.引擎&#xff08;Engine&#xff09; – Scrapy的引擎是控制数据流和触发事件的核心。它管理着Spider发送的请求和接收的响应&#xff0c;以及处理Spider生成的Item。引擎是Scrapy运行的驱动力。…

【0基础学爬虫】爬虫框架之 feapder 的使用

前言 大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到难全方位覆盖&#xff0c;特设【0…

SIFT 3D 点云关键点

检测原理 该算法在尺度空间中寻找极值点并提取出其位置、 尺度、 旋转不变量信息&#xff0c;提取的特征对视角变化、 仿射变换、 噪声具有一定的鲁棒性&#xff0c;对尺度缩放、 旋转具有较好的不变性。 SIFT关键点检测主要包括生成尺度空间构建、 空间极值点检测、 稳定关键…

Nacos 2.x 系列【18】多网卡 IP 配置

文章目录 1. 前言2. 服务端3. 客户端 1. 前言 个人电脑或者服务器&#xff0c;存在多网卡环境时&#xff0c;Nacos 可能会存在IP不正确问题。 2. 服务端 Nacos 服务在启动的时候需要选择运行时使用的IP或者网卡&#xff0c;在启动时&#xff0c;可以看到打印了IP&#xff1a…