ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

4 禁忌搜索

2020-04-23 23:04:31  阅读:2017  来源: 互联网

标签:__ temp self 禁忌 cost 搜索 nTimes


4 禁忌搜索

4.1 概述

禁忌搜索(Tabu Search,TS)是美国科罗拉多州大学的Fred Glover教授于1986年提出的搜索算法,是一个可以用来跳出局部最优的搜索方法。TS从一个初始解出发,按照一定的策略往一个方向进行搜索。同时为了避免局部最小值TS还引入了“记忆机制”,记住已经选过的解,如果下次出现了在自己记忆中出现过的解,TS就不会去选择它,当然经过了一段时间后,记忆中较老的解将会被遗忘。

在TS中,这个记忆机制就称为禁忌表,在禁忌表中的解表明该解先前已经被选择过了,禁止被重复选择。而遗忘操作则是将解从禁忌表中删除。

4.2 具体算法细节

这里通过一个4个城市的对称TSP问题来说明禁忌搜索的具体细节:

解形式的定义:

我们将解定义为A,B,C,D组成的字符序列,即\(ABCD\)表示从A城市出发,先经过B,然后经过C,再经过D,最后回到城市A。

解的形式依赖于具体的问题,不同解的定义将直接影响到后续算法的实现。

新解(邻域)的产生:

这里我们定义一个邻域产生函数\(candidate(\cdot)\),该函数接受一个序列,然后输出该序列的邻域。为了产生新的解,我们需要定义一个解相邻的解应该是怎么样的。一种可行的方式是将当前的解两个相邻的城市互换得到新的解,即\(ABCD\)的邻域为

\[candidate(ABCD)=\{BACD,ACBD,ABDC\} \]

当然也可以选择其他方式产生新的解。

禁忌表设置:

禁忌表的两个重要因素是禁忌对象(被放入到禁忌表中的对象)以及禁忌长度(禁忌对象多长时间后从禁忌表中移除)。这里我们将禁忌对象选取为问题的解,禁忌长度设置为2。

初始化禁忌表\(H=\emptyset\),以及一个初始解\(x=ABCD\),历史最优解初始化为\(x_b=ABCD\),代价记为\(cost_b=10\)。

(1)第一次迭代

先产生当前解的邻域\(candidate(ABCD)=\{BACD,ACBD,ABDC\}\),然后选择该邻域内代价最小的解\(BACD\),其代价为13,所以不更新历史最优解。最后,更新禁忌表\(H=\{(BACD,2)\}\),这里禁忌表中的数字每经过一次迭代数字减一,归零时移除相应的禁忌对象。

(2)第二次迭代

先产生当前解的邻域\(candidate(BACD)=\{ABCD,BCAD,BADC\}\),然后选择该邻域内代价最小的解\(ABCD\),其代价为10,所以不更新历史最优解。最后,更新禁忌表\(H=\{(BACD,1),(ABCD,2)\}\)。

类似地,不断迭代搜索最优解直到满足算法的终止条件,最终输出历史最优解。

实际上,在第二次迭代中\(BADC\)的代价也是10,具体要选择哪一个解,可以根据自己的倾向随机选一个即可。在这个示例过于特殊,因为初始解实际上就是一个最优解。如果不让初始解马上又被选中,可以在初始化的时候就把初始解加入到禁忌表中。

还需要注意的是:

(1)不要仅仅局限于将解作为禁忌对象,禁忌对象可以是其他任何合理的元素。例如,我们也可以将上述例子中代价值作为禁忌对象。此时,如果有一个解的代价值存在于禁忌表中,那么该解就不能被选中,这就强迫算法只能去选择具有不同代价的解。

(2)禁忌长度需要合理选择,禁忌长度太短容易陷入局部最优点,无法跳出;太长容易造成计算时间较大,更糟的情况是计算无法继续进行下去。一种策略是禁忌长度是一个可变的数,当禁忌表新加入一个禁忌对象时,就为其禁忌长度随机设置一个位于\([N_1,N_2]\)之间的整数值。

(3)在实际搜索中,可能出现所有候选解都处于禁止选中的状态,为了能够让算法能够正常继续下去,就需要引入特赦准则。特赦准则允许我们让禁忌表中代价最小(或某种指标最好)的对象能够被重新选中。从这个角度来看,这相当于加强了对某个较优局部的搜索,以希望发现更好的解。

终止条件:

  1. 设置最大迭代次数。
  2. 采用频率控制,当某个解、目标值或元素序列出现的频率高于给定的阈值,停止算法。
  3. 如果在给定的迭代次数内,历史最优值未发生变化,可以停止算法。

算法描述:

有了上述的讨论,禁忌搜索可以描述为

  1. 初始化一个初始解\(x_0\),禁忌表置为空集,记录历史最优解,以及其他所需的参数。
  2. 判断是否满足停止规则,如果不满足,那么就就从候选集\(candidate(x_0)\)中选取一个评价指标最优的解,作为当前解。然后更新禁忌表,重复执行第2步。

4.3 算法实现与测试

测试5城市非对称的TSP问题

代码实现:

import numpy as np
import operator


class Solution:
    __slots__ = ('value', 'cost')

    def __init__(self, value, cost):
        self.value = value
        self.cost = cost


class Item:
    __slots__ = ('solution', 'nTimes')

    def __init__(self, solution, nTimes):
        self.solution = solution
        self.nTimes = nTimes


class TS:
    def __init__(self, nCities, nTimes, num_iter, dist):
        self.nCities = nCities
        self.nTimes = nTimes
        self.num_iter = num_iter
        self.dist = dist
        self.best = None
        self.table = []

    def getRandomSolution(self):
        temp = [i for i in range(self.nCities)]
        np.random.shuffle(temp)
        return Solution(temp, self.getCost(temp))

    def getCost(self, x):
        cost = 0
        for i in range(self.nCities):
            cost += self.dist[x[i], x[(i + 1) % self.nCities]]
        return cost

    def get_candidates(self, x):
        cans = []
        for i in range(self.nCities - 1):
            temp = x.value[:]
            temp[i], temp[i + 1] = temp[i + 1], temp[i]
            cans.append(Solution(temp, self.getCost(temp)))
        return cans

    def update_table(self):
        delete = []
        for item in self.table:
            item.nTimes -= 1
            if item.nTimes == 0:
                delete.append(item)
        for item in delete:
            self.table.remove(item)

    def isAvailable(self, x):
        for item in self.table:
            if operator.eq(item.solution.value, x.value):
                return False
        return True

    def solve(self):
        # 初始化
        x = self.getRandomSolution()
        self.best = x
        self.table.append(Item(x, self.nTimes))
        # 开始搜索
        for _ in range(self.num_iter):
            self.update_table()
            cans = self.get_candidates(x)
            while cans:
                x = min(cans, key=lambda s: s.cost)
                if self.isAvailable(x):
                    self.table.append(Item(x, self.nTimes))
                    break
                else:
                    cans.remove(x)
            else:
                # 特赦
                x = min(self.table, key=lambda i: i.solution.cost).solution
            if x.cost < self.best.cost:
                self.best = x
        return self.best


if __name__ == '__main__':
    dist = np.array([
        [0, 1, 5, 4, 3],
        [3, 0, 2, 6, 1],
        [5, 2, 0, 1, 4],
        [1, 6, 3, 0, 3],
        [3, 3, 1, 4, 0]
    ])
    ts = TS(
        nCities=5,
        nTimes=3,
        num_iter=10,
        dist=dist
    )
    s = ts.solve()
    print(s.value)
    print(s.cost)

测试结果:

这里[0,1,4,2,3]表示访问城市的顺序为:0->1->4->2->3->0

标签:__,temp,self,禁忌,cost,搜索,nTimes
来源: https://www.cnblogs.com/philolif/p/TS.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有