【优化算法】Greedy Randomized Adaptive Search算法 超详细解析,附代码实现TSP问题求解

01 概述

Greedy Randomized Adaptive Search,贪婪随机自适应搜索(GRAS),是组合优化问题中的多起点元启发式算法,在算法的每次迭代中,主要由两个阶段组成:构造(construction)和局部搜索( local search)。 构造(construction)阶段主要用于生成一个可行解,而后该初始可行解会被放进局部搜索进行邻域搜索,直到找到一个局部最优解为止。

02 整体框架

如上面所说,其实整一个算法的框架相对于其他算法来说还算比较简单明了,大家可以先看以下整体的伪代码:

GRAS主要由两部分组成:

  • Greedy_Randomized_Construction:在贪心的基础上,加入一定的随机因素,构造初始可行解。
  • Local Search:对上面构造的初始可行解进行邻域搜索,直到找到一个局部最优解。

然后再多说两句:

  1. Repair是什么鬼?
    有时候由于随机因素的加入,Greedy_Randomized_Construction阶段生成的解不一定都是可行解,所以为了保证下一步的Local Search能继续进行,加入repair算子,对解进行修复,保证其可行。
  2. 不是说自适应(Adaptive)吗?我怎么没看到Adaptive 的过程?
    别急,这个后面具体举例的时候会详细讲到。

03 举个例子说明

为了大家能更加深入理解该算法,我们举一个简单的例子来为大家详细讲解算法的流程。

好了,相信大家都看懂上面的问题了(看不懂也别问我,摊手)。对于上述问题,我们来一步一个脚印用GRAS来求解之,来,跟紧小编的脚步……

强调了很多次,GRAS由两部分组成:Greedy_Randomized_Construction和Local Search,所以,在求解具体问题的时候,完成这两部分的设计,然后按照第二节所示的框架搭起来就可以。

3.1 Greedy_Randomized_Construction

这里还是老规矩,先上伪代码给大家看看,然后我们再进行讲解,毕竟对于算法来说,伪代码的作用不言而喻。

  • 第1行,一开始解是一个空集。
  • 第2行,初始化候选元素的集合,这里候选元素是指能放进Solution的元素(也就是目前Solution里面没有的),比如1,2,3……。
  • 第3行,对候选集合的每个元素进行评估,计算将元素x放入Solution会导致目标函数f改变量delta_x。
  • 第5行,根据delta_x对各个元素排序,选取部分较好的候选元素组成RCL表(贪心性体现在这里)。
  • 第6行,随机在RCL中选取一个元素放进Solution。(算法的随机性)
  • 第8、9行,更新候选元素集合,然后对每个元素进行重新评估计算delta值。(算法的自适应性体现在这里)

相信经过上面如此详细的介绍,大家都懂了吧!

3.2 Local Search

关于Local Search方面的内容,相信大家学习heuristic这么久了,就不用我多说什么了吧:

简单看一下伪代码即可,主要是邻域算子的设计,然后就是在邻域里面进行搜索,找到一个局部最优解为止。然后关于邻域搜索,有best-improving or first-improving strategy 两种策略,这个下次有时间出个专题给大家讲明白一些相关概念吧。

04 再论Greedy_Randomized_Construction

前面我们说了,Greedy_Randomized_Construction用于生成初始解,既然是Greedy_Randomized两个结合体,那么肯定就有一个权重分配的问题,即,是Greedy成分多一点呢?还是Randomized成分多一点好呢?因此,为了控制这两个小老弟的权重,防止某个家伙在该过程中用力过猛导致解不那么好的情况,我们引入一个参数α:

其他部分就不再多说,可以看到,上面的α参数主要是控制RCL的长度:

  • 当α=0时,纯贪心,只能选取最优的候选元素。
  • 当α=1时,纯随机,所有候选元素都可随机选。

05 代码实现

由于小编精力有限,就不从头写一遍了,从GitHub上找了一个感觉还不错的算法给大家,也是求解TSP问题的。不过说实在的,python写算法的速度是很慢的,无论是速度还是算法架构等方面都不推荐大家用matlab或者python写大型优化算法。

运行结果如下:

代码算例以及相关运行结果请关注公众号【程序猿声】,后台回复:GRAS,即可下载

############################################################################

# Created by: Prof. Valdecy Pereira, D.Sc.
# UFF - Universidade Federal Fluminense (Brazil)
# email:  [email protected]
# Course: Metaheuristics
# Lesson: Local Search-GRASP

# Citation:
# PEREIRA, V. (2018). Project: Metaheuristic-Local_Search-GRASP, File: Python-MH-Local Search-GRASP.py, GitHub repository: <https://github.com/Valdecy/Metaheuristic-Local_Search-GRASP>

############################################################################

# Required Libraries
import pandas as pd
import random
import numpy  as np
import copy
import os
from matplotlib import pyplot as plt 

# Function: Tour Distance
def distance_calc(Xdata, city_tour):
    distance = 0
    for k in range(0, len(city_tour[0])-1):
        m = k + 1
        distance = distance + Xdata.iloc[city_tour[0][k]-1, city_tour[0][m]-1]
    return distance

# Function: Euclidean Distance
def euclidean_distance(x, y):
    distance = 0
    for j in range(0, len(x)):
        distance = (x.iloc[j] - y.iloc[j])**2 + distance
    return distance**(1/2) 

# Function: Initial Seed
def seed_function(Xdata):
    seed = [[],float("inf")]
    sequence = random.sample(list(range(1,Xdata.shape[0]+1)), Xdata.shape[0])
    sequence.append(sequence[0])
    seed[0] = sequence
    seed[1] = distance_calc(Xdata, seed)
    return seed

# Function: Build Distance Matrix
def buid_distance_matrix(coordinates):
    Xdata = pd.DataFrame(np.zeros((coordinates.shape[0], coordinates.shape[0])))
    for i in range(0, Xdata.shape[0]):
        for j in range(0, Xdata.shape[1]):
            if (i != j):
                x = coordinates.iloc[i,:]
                y = coordinates.iloc[j,:]
                Xdata.iloc[i,j] = euclidean_distance(x, y)
    return Xdata

# Function: Tour Plot
def plot_tour_distance_matrix (Xdata, city_tour):
    m = Xdata.copy(deep = True)
    for i in range(0, Xdata.shape[0]):
        for j in range(0, Xdata.shape[1]):
            m.iloc[i,j] = (1/2)*(Xdata.iloc[0,j]**2 + Xdata.iloc[i,0]**2 - Xdata.iloc[i,j]**2)
    m = m.values
    w, u = np.linalg.eig(np.matmul(m.T, m))
    s = (np.diag(np.sort(w)[::-1]))**(1/2)
    coordinates = np.matmul(u, s**(1/2))
    coordinates = coordinates.real[:,0:2]
    xy = pd.DataFrame(np.zeros((len(city_tour[0]), 2)))
    for i in range(0, len(city_tour[0])):
        if (i < len(city_tour[0])):
            xy.iloc[i, 0] = coordinates[city_tour[0][i]-1, 0]
            xy.iloc[i, 1] = coordinates[city_tour[0][i]-1, 1]
        else:
            xy.iloc[i, 0] = coordinates[city_tour[0][0]-1, 0]
            xy.iloc[i, 1] = coordinates[city_tour[0][0]-1, 1]
    plt.plot(xy.iloc[:,0], xy.iloc[:,1], marker = 's', alpha = 1, markersize = 7, color = 'black')
    plt.plot(xy.iloc[0,0], xy.iloc[0,1], marker = 's', alpha = 1, markersize = 7, color = 'red')
    plt.plot(xy.iloc[1,0], xy.iloc[1,1], marker = 's', alpha = 1, markersize = 7, color = 'orange')
    return

# Function: Tour Plot
def plot_tour_coordinates (coordinates, city_tour):
    coordinates = coordinates.values
    xy = pd.DataFrame(np.zeros((len(city_tour[0]), 2)))
    for i in range(0, len(city_tour[0])):
        if (i < len(city_tour[0])):
            xy.iloc[i, 0] = coordinates[city_tour[0][i]-1, 0]
            xy.iloc[i, 1] = coordinates[city_tour[0][i]-1, 1]
        else:
            xy.iloc[i, 0] = coordinates[city_tour[0][0]-1, 0]
            xy.iloc[i, 1] = coordinates[city_tour[0][0]-1, 1]
    plt.plot(xy.iloc[:,0], xy.iloc[:,1], marker = 's', alpha = 1, markersize = 7, color = 'black')
    plt.plot(xy.iloc[0,0], xy.iloc[0,1], marker = 's', alpha = 1, markersize = 7, color = 'red')
    plt.plot(xy.iloc[1,0], xy.iloc[1,1], marker = 's', alpha = 1, markersize = 7, color = 'orange')
    return

# Function: Rank Cities by Distance
def ranking(Xdata, city = 0):
    rank = pd.DataFrame(np.zeros((Xdata.shape[0], 2)), columns = ['Distance', 'City'])
    for i in range(0, rank.shape[0]):
        rank.iloc[i,0] = Xdata.iloc[i,city]
        rank.iloc[i,1] = i + 1
    rank = rank.sort_values(by = 'Distance')
    return rank

# Function: RCL
def restricted_candidate_list(Xdata, greediness_value = 0.5):
    seed = [[],float("inf")]
    sequence = []
    sequence.append(random.sample(list(range(1,Xdata.shape[0]+1)), 1)[0])
    for i in range(0, Xdata.shape[0]):
        count = 1
        rand = int.from_bytes(os.urandom(8), byteorder = "big") / ((1 << 64) - 1)
        if (rand > greediness_value and len(sequence) < Xdata.shape[0]):
            next_city = int(ranking(Xdata, city = sequence[-1] - 1).iloc[count,1])
            while next_city in sequence:
                count = np.clip(count+1,1,Xdata.shape[0]-1)
                next_city = int(ranking(Xdata, city = sequence[-1] - 1).iloc[count,1])
            sequence.append(next_city)
        elif (rand <= greediness_value and len(sequence) < Xdata.shape[0]):
            next_city = random.sample(list(range(1,Xdata.shape[0]+1)), 1)[0]
            while next_city in sequence:
                next_city = int(random.sample(list(range(1,Xdata.shape[0]+1)), 1)[0])
            sequence.append(next_city)
    sequence.append(sequence[0])
    seed[0] = sequence
    seed[1] = distance_calc(Xdata, seed)
    return seed

# Function: 2_opt
def local_search_2_opt(Xdata, city_tour):
    tour = copy.deepcopy(city_tour)
    best_route = copy.deepcopy(tour)
    seed = copy.deepcopy(tour)
    for i in range(0, len(tour[0]) - 2):
        for j in range(i+1, len(tour[0]) - 1):
            best_route[0][i:j+1] = list(reversed(best_route[0][i:j+1]))
            best_route[0][-1]  = best_route[0][0]
            best_route[1] = distance_calc(Xdata, best_route)
            if (best_route[1] < tour[1]):
                tour[1] = copy.deepcopy(best_route[1])
                for n in range(0, len(tour[0])):
                    tour[0][n] = best_route[0][n]
            best_route = copy.deepcopy(seed)
    return tour

# Function: GRASP
def greedy_randomized_adaptive_search_procedure(Xdata, city_tour, iterations = 50, rcl = 25, greediness_value = 0.5):
    count = 0
    best_solution = copy.deepcopy(city_tour)
    while (count < iterations):
        rcl_list = []
        for i in range(0, rcl):
            rcl_list.append(restricted_candidate_list(Xdata, greediness_value = greediness_value))
        candidate = int(random.sample(list(range(0,rcl)), 1)[0])
        city_tour = local_search_2_opt(Xdata, city_tour = rcl_list[candidate])
        while (city_tour[0] != rcl_list[candidate][0]):
            rcl_list[candidate] = copy.deepcopy(city_tour)
            city_tour = local_search_2_opt(Xdata, city_tour = rcl_list[candidate])
        if (city_tour[1] < best_solution[1]):
            best_solution = copy.deepcopy(city_tour)
        count = count + 1
        print("Iteration =", count, "-> Distance =", best_solution[1])
    print("Best Solution =", best_solution)
    return best_solution

######################## Part 1 - Usage ####################################

X = pd.read_csv('Python-MH-Local Search-GRASP-Dataset-01.txt', sep = '\t') #17 cities = 1922.33
seed = seed_function(X)
lsgrasp = greedy_randomized_adaptive_search_procedure(X, city_tour = seed, iterations = 5, rcl = 5, greediness_value = 0.5)
plot_tour_distance_matrix(X, lsgrasp) # Red Point = Initial city; Orange Point = Second City # The generated coordinates (2D projection) are aproximated, depending on the data, the optimum tour may present crosses.

Y = pd.read_csv('Python-MH-Local Search-GRASP-Dataset-02.txt', sep = '\t') # Berlin 52 = 7544.37
X = buid_distance_matrix(Y)
seed = seed_function(X)
lsgrasp = greedy_randomized_adaptive_search_procedure(X, city_tour = seed, iterations = 10, rcl = 15, greediness_value = 0.5)
plot_tour_coordinates (Y, lsgrasp) # Red Point = Initial city; Orange Point = Second City

原文地址:https://www.cnblogs.com/dengfaheng/p/10977528.html

时间: 2024-09-29 19:42:50

【优化算法】Greedy Randomized Adaptive Search算法 超详细解析,附代码实现TSP问题求解的相关文章

【智能算法】粒子群算法(Particle Swarm Optimization)超详细解析+入门代码实例讲解

喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 01 算法起源 粒子群优化算法(PSO)是一种进化计算技术(evolutionary computation),1995 年由Eberhart 博士和kennedy 博士提出,源于对鸟群捕食的行为研究 .该算法最初是受到飞鸟集群活动的规律性启发,进而利用群体智能建立的一个简化模型.粒子群算法在对动物集群活动行为观察基础上,利用群体中的个体对信息的共享使整个群体的运动在问题求解空间中产生从无序到有序的演化过程,从而获得最优解.

算法核心——空间复杂度和时间复杂度超详细解析

算法核心——空间复杂度和时间复杂度超详细解析 一.什么是算法 算法: 一个有限指令集 接受一些输入(有些情况下不需要收入) 产生输出 一定在有限步骤之后终止 每一条指令必须: 有充分明确的目标,不可以有歧义 计算机能处理的范围之内 描述应不依赖于任何一种计算机语言以及具体的实现手段 其实说白了,算法就是一个计算过程解决问题的方法.我们现在已经知道数据结构表示数据是怎么存储的,而“程序=数据结构+算法”,数据结构是静态的,算法是动态的,它们加起来就是程序. 对算法来说有输入,有输出,相当于函数有参

JFinal 源码超详细解析之DB+ActiveRecord

我记得以前有人跟我说,"面试的时候要看spring的源码,要看ioc.aop的源码"那为什么要看这些开源框架的源码呢,其实很多人都是"应急式"的去读,就像读一篇文章一下,用最快的速度把文章从头到尾读一遍,那结果就是当你读完它,你也不清楚它讲了一个什么故事,想表达什么. 一个优秀的架构的源码我认为就好像一本名著一样,你的"文学"水平越高,你就越能读出作者设计的精妙之处.一篇源码在你不同水平的时候,能读出不同的东西,因此,我觉得优秀的框架的源码是经久

【数据结构】10分钟教你用栈求解迷宫老鼠问题超详细教程附C++源代码

问题描述 给定一张迷宫地图和一个迷宫入口,然后进入迷宫探索找到一个出口.如下图所示: 该图是一个矩形区域,有一个入口和出口.迷宫内部包含不能穿越的墙壁或者障碍物.这些障碍物沿着行和列放置,与迷宫的边界平行.迷宫的入口在左上角,出口在右下角. 问题分析 首先要有一张迷宫地图,地图由两部分组成: (1)一是迷宫中各处的位置坐标, (2)二是迷宫各位置处的状态信息,即该处是墙还是路 所以,该迷宫地图可由一个二维数组来表示.数组的横纵坐标表示迷宫各处的位置坐标,数组元素表示各位置处的状态信息. 2.在这

【算法】变邻域搜索算法(Variable Neighborhood Search,VNS)超详细一看

更多精彩尽在微信公众号[程序猿声] 变邻域搜索算法(Variable Neighborhood Search,VNS)一看就懂的解析 00 目录 局部搜索再次科普 变邻域搜索 造轮子写代码 01 局部搜索科普三连 虽然之前做的很多篇启发式的算法都有跟大家提过局部搜索这个概念,为了加深大家的印象,在变邻域主角登场之前还是给大家科普一下相关概念.热热身嘛- 1.1 局部搜索是什么玩意儿? 官方一点:局部搜索是解决最优化问题的一种启发式算法.对于某些计算起来非常复杂的最优化问题,比如各种NP完全问题,

算法基础(八):超详细最优二叉树构建(1)

赫夫曼(Huffman)树也称最有二叉树,是一类带全路径长度最短的树,有着广泛的应用.比如一棵判定树,根据学生的成绩划分及格还是不及格还是优.中等.良好.显然用if-else或者switch就可以简单实现,当然可以直接毫不考虑的直接这样写,但是如果我们再肯花点功夫,就可以得到更加高效的程序.我们可以以学生的总的学科分数的占的各个分数段的比率为权,构造一棵赫夫曼树,这样可以减少比较次数,提高程序运行效率. 构造赫夫曼树的方法步骤其实很简单--赫夫曼算法: (1). 根据给定的n个权值{w1,w2,

算法基础(九):超详细最优二叉树构建(2)求编码

从叶子到跟逆向求每个字符的赫夫曼编码: void GetHuffmanCode(HuffmanTree &HT,HuffmanCode &HC,int n) { char* cd; int c; int f; int start; int i = 0; //从叶子到根逆向求每个字符的哈夫曼编码 printf("\n进入求编码函数..."); HC = (HuffmanCode)malloc( (n+1) * sizeof(char*)); //分配n个字符编码的头指针向

[超详细] Apache网页优化:网页压缩与网页缓存超详细

前言 前两篇文章讲了Aapche的访问控制与日志分析,这篇文章会重点讲Apache的网页优化,比如:网页的压缩.网页的缓存.我们在使用Aapche作为Web服务器的过程中,只有对Aapche服务器进行适当的优化配置,才能让它发挥出更好好的性能,因此,我们需要对Aapche服务器的配置优化! 网页压缩 凡是总会有个为什么,我们为什么要对网页进行压缩,带着这个问题来看接下来的操作.我们也常常上网,会知道网站的访问速度的快慢有很多因素,比如:应用程序的响应速度.网络带宽.服务器性能等!最重要的一个因素

超详细的Xcode代码格式化教程,可自定义样式

为什么要格式化代码 当团队内有多人开发的时候,每个人写的代码格式都有自己的喜好,也可能会忙着写代码而忽略了格式的问题. 在之前,我们可能会写完代码后,再一点一点去调格式,很浪费时间. 有了ClangFormat插件后,就可以一键把代码格式化成统一的样式,不仅节省了时间,也使得代码更规范.我们还可以定制自己喜欢的样式. 安装ClangFormat插件 可以手动安装(下载GitHub项目编译),也可以用Alcatraz(插件管理器)安装,都很简单,具体可以看我的文章<Xcode方便开发的插件推荐>