首页 > 其他分享 >如何用随机方法求解组合优化问题(七)

如何用随机方法求解组合优化问题(七)

时间:2023-08-21 23:56:42浏览次数:45  
标签:seq idx 求解 int self cdots 随机 pi 优化

模拟退火算法应用举例

这是一篇笔记,是对于B站up主马少平的视频(第四篇 如何用随机方法求解组合优化问题(七))的学习与记录。

旅行商问题

一个商人要访问 \(n\) 个城市,每个城市访问一次,并且只能访问一次,最后再回到出发城市。
问如何规划才能使得行走的路径长度最短。

旅行商问题的解空间非常大,难以使用穷举法进行求解。

  • 10城市:\(10!=3628800\)
  • 20城市:\(20!\approx 2.43\times 10^{18}\)

在已知模拟退火算法的基本框架,以及需要确定的参数的情况下,为了解决实际问题,我们需要将实际问题,转换并抽离出我们需要的参数。

指标函数

\[f(\pi_1,\cdots,\pi_n)=\sum\limits_{i=1}^nd_{\pi_i\pi_{i+1}}\quad\quad \pi_{n+1}=\pi_1 \]

其中 \((\pi_1,\cdots,\pi_n)\) 是表示路径的序列,\(\pi_i\) 表示第 \(i\) 个访问的城市,\(d_{\pi_i\pi_i+1}\) 是路径中相邻两个城市的距离。

新解的产生

逆序交换法
  • 当前解

\[(\pi_1,\cdots,\pi_u,\pi_{u+1},\cdots,\pi_{v-1},\pi_v,\cdots,\pi_n)\quad\quad u\ge0,v\le n+1 \]

  • 交换后

\[(\pi_1,\cdots,\pi_u,\pi_{v-1},\cdots,\pi_{u+1},\pi_v,\cdots,\pi_n)\quad\quad u\ge0,v\le n+1 \]

即 \(\pi_u\) 和 \(\pi_v\) 这两个城市之间的路径进行逆序, \(\pi_u\) 和 \(\pi_v\) 不变。

指标函数差

当新解是较好的解时,百分之百接受;当新解是较差的解时,则按概率接受:

\[P_{i\Rightarrow j}(t)=e^{-\frac{f(j)-f(i)}{t}}=e^{-\frac{\Delta f}{t}} \]

为了计算接受概率,需要先计算指标函数差 \(f(j)-f(i)\)。

需要注意的是,这里并不需要完全计算出两个解的总路径长度再做差。

由于我们使用的是逆序交换法,两个解之间的差别只存在于逆序区间的交界处

当前解:\((\pi_1,\cdots,\pi_u,\pi_{u+1},\cdots,\pi_{v-1},\pi_v,\cdots,\pi_n)\)

交换后:\((\pi_1,\cdots,\pi_u,\pi_{v-1},\cdots,\pi_{u+1},\pi_v,\cdots,\pi_n)\)

因此,有:

\[\begin{align*} \Delta f &= f(j) - f(i) \\ &= (d_{\pi_u\pi_{v-1}}+d_{\pi_{u+1}\pi_v})-(d_{\pi_u\pi_{u+1}}+d_{\pi_{v-1}\pi_v}) \end{align*} \]

故从解 \(i\) 到解 \(j\) 的接受概率:

\[P_{i\Rightarrow j}(t)= \begin{cases} 1 & if \ \Delta f\le 0 \\ e^{-\frac{\Delta f}{t}} & else \end{cases} \]

参数确定

  • 初始温度:\(t_0=200\)
  • 温度衰减系数:\(\alpha=0.95\),\(t_{k+1}=\alpha t_k\)
  • 每个温度下的迭代次数:\(100n\),\(n\) 为城市数
  • 算法结束条件:无变化控制法(相邻两个温度下结果相等)

代码实现

测试数据

这里提供了 10 个城市的 xy 坐标。

A 0.4000 0.4439
B 0.2439 0.1463
C 0.1707 0.2293
D 0.2293 0.7610
E 0.5171 0.9414
F 0.8732 0.6536
G 0.6878 0.5219
H 0.8488 0.3609
I 0.6683 0.2536
J 0.6195 0.2634

City class

定义 City 类,方便后续操作。

class City:
    def __init__(self, name: str, x: float, y: float):
        self.name = name
        self.x = x
        self.y = y

    def __repr__(self):
        return f"{self.name} {self.x} {self.y}"

    def __str__(self):
        return f"{self.name} {self.x} {self.y}"

    def distance(self, other: 'City'):
        d = ((self.x - other.x)*(self.x - other.x)
             + (self.y - other.y)*(self.y - other.y))**0.5
        return d

读取数据

假设上面的测试数据保存在10.txt文件中。这里用一个函数来读取文件数据,并转换为 City 列表。

def read_data(path: str):
    data: list[City] = []
    with open(path, 'r', encoding='utf-8') as f:
        for line in f.readlines():
            information = line.replace('\n', '').split(' ')
            data.append(City(name=information[0], x=float(information[1]), y=float(information[2])))
    return data

生成随机序列

为了随机地生成一个初始解,使用一个数值列表表示旅行商的城市访问顺序:

def gen_random_seq(length: int)->list[int]:
    sequence = list(range(length))
    random.shuffle(sequence)
    return sequence

实现对序列片段的逆序交换

为了生成邻居解,需要实现逆序交换,函数需要传入:原序列、逆序区间的起始下标、结束下标。

def reverse_seq(sequence: list[int], start_idx: int, end_idx: int)->list[int]:
    assert start_idx <= end_idx
    i = start_idx
    j = end_idx
    while i<j:
        sequence[i], sequence[j] = sequence[j], sequence[i]
        i += 1
        j -= 1
    return sequence

生成邻居解

根据传入的原序列,使用逆序交换法生成邻居解,逆序区间是随机的。

这里生成两个端点ab之后,进行逆序,但是逆序操作并不包含这两个端点。

因为类似于[0,...,9][9,...,0]这两个序列在旅行商问题中是一样的,路线是首尾相接的环。

def gen_near_seq(sequence:list[int])->(list[int], int, int):
    n = len(sequence)
    a = math.floor(random.random()*n)
    b = math.floor(random.random()*n)
    start_idx, end_idx = (a, b) if a<b else (b, a)
    # 除去区间端点
    if end_idx - start_idx > 1:
        start_idx += 1
        end_idx -= 1
    return reverse_seq(sequence, start_idx, end_idx), start_idx, end_idx

指标函数

根据序列和城市列表,计算路径的总长度。

def valuate(sequence: list[int], cities: list[City])->float:
    total: float = 0
    length = len(cities)
    for idx in range(length):
        curr_city = cities[sequence[idx]]
        next_city = cities[sequence[(idx+1)%length]]
        d = curr_city.distance(next_city)
        total += d
    return total

最终实现

根据上面已经编写好的函数,开始结合算法解决问题。

数据读取
# 文件的路径根据实际情况进行填写
cities = read_data('./dataset/10.txt')
超参数设置
# 初始温度
t = 200
# 温度递减速率
alpha = 0.95
# 问题规模(10个城市)
n = 10
# 同一温度的迭代次数
iteration_times = 100 * n
生成随机序列

随机地生成当前的序列和它的指标:curr_seqcurr_val

初始化最优解的序列和指标:best_seqbest_val.

curr_seq: list[int] = gen_random_seq(n)
curr_val: float = valuate(curr_seq, cities)
best_seq: list[int] = list()
best_val: float = float('inf')
内外循环
while curr_val != best_val:
    i = 0
    while i < iteration_times:
        i += 1
        # 生成相邻解
        new_seq, start_idx, end_idx = gen_near_seq(curr_seq)
        new_val = valuate(new_seq, cities)
        # 是否接受解
        if new_val <= curr_val:
            curr_seq, curr_val = new_seq, new_val
        else:
            if random.random() < math.e ** ((curr_val - new_val) / t):
                curr_seq, curr_val = new_seq, new_val
        # 记录当前温度的最优解
        if curr_val < best_val:
            best_seq, best_val = curr_seq, curr_val
    t *= alpha

运行结果与相关图像

上述 10 个城市的案例,最终结果为

best_val: 2.6906706370094136

通过在上面代码的循环中嵌入“导出数据到csv文件”的操作(这里没有给出代码),然后结合pandas和matplotlib等库,可以绘制出下面图像:

image-20230821231608416

这个图像的横坐标是迭代次数,纵坐标是指标(即路径长度)。

上图的横坐标数据中,每一个温度记录10000个数据,直到最终满足外循环的结束条件。

可以看到,随着温度的下降,波动在变小,并最终收敛到最优解。

前段区间的波动一致,是因为指标本身存在上下界,即十个城市的坐标确认后,最优解和最差解是固定的。

通过减少数据导出频率(提高运行速度),并且将温度的数值进行等比例缩小。将温度和指标绘制到下面这幅图:

image-20230821233329899

可以更直观地看出温度和指标波动的关系。

标签:seq,idx,求解,int,self,cdots,随机,pi,优化
From: https://www.cnblogs.com/feixianxing/p/combinatorial-problem-random-method-solution-7.html

相关文章

  • 【学习笔记】优化建图相关(线段树优化,倍增优化)
    优化建图发现并没有人写得很详细的样子,那我也摆烂好惹点击查看目录目录前言线段树优化建图单点连区间区间连区间例题解题:倍增优化建图例题解题:前言众所周知,连边的时间复杂度一般是\(O(1)\),但,当连边的对象是一个连续的树上区间的时候,我们或许有更优的连边方式:优化建图。......
  • 谷歌优化“每次点击费用人工出价策略”
    借助每次点击费用(CPC)人工出价,您可以设置愿意为用户每次点击广告所支付的最高价格。这种出价方式可谓是物有所值,因为只有当用户对您的广告有足够的兴趣,点击广告并了解详情时,您才需要付费。以广告牌为例,广告客户根据可能会有多少人在开车路过时看到其广告来支付广告牌空间的费用,......
  • 多元分类预测 | Matlab鲸鱼优化算法优化深度极限学习机(WOA-DELM)分类预测
     ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 多元分类预测 | Matlab粒子群优化算法优化深度极限学习机(PSO-DELM)分类预测
     ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 多元分类预测 | Matlab灰狼优化算法优化深度极限学习机(GWO-DELM)分类预测
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 路径规划算法:基于指数分布优化的机器人路径规划算法- 附matlab代码
     ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • Python代理延迟突增故障定位和优化方法
     在进行网络爬虫和数据采集时,代理延迟突增是一个常见的问题,影响爬虫的效率和稳定性。本文将详细分析Python代理延迟突增故障的定位和优化方法,并提供实际操作价值的解决方案。 代理延迟突增可能由以下原因引起: 1.代理服务器性能问题:代理服务器可能存在负载过高、响应速度慢......
  • 【图论#02】岛屿数量,flood fill算法的代码实现与优化
    岛屿数量给你一个由'1'(陆地)和'0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。示例1:输入:grid=[["1","1","1","1","0"],["1","1"......
  • Vue 项目性能优化实践
    Vue已经成为前端必备利器了,Vue首页加载速度慢是很常见的问题,dist文件的体积差不多都在10m左右,解决方式也有很多,最简单的方式增加服务器的配置性能,当然在预算有限的情况下,如果通过优化来提升速度呢。比如在一台普通配置服务器上,差不多加载速度在15s左右,那是没办法接受的,不管是用......
  • DP优化
    DP的效率取决于\(3\)方面:1.状态总数2.每个状态的决策数3.状态转移计算量。这三方面都可以进行优化。状态总数优化:相当于搜索的剪枝,去除无用状态;使用降维,设计DP状态时尽量使用低维的DP。决策数量优化:即状态转移方程的优化,减少决策数量,如斜率优化,四边形不等式优化。......