运筹系列82:使用动态规划求解TSP问题

这篇具有很好参考价值的文章主要介绍了运筹系列82:使用动态规划求解TSP问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 动态规划思路和小技巧

定义 c ( s , k ) c(s,k) c(s,k)为当前在 k k k,待访问点的集合 s s s,最后返回城市0的最短路径,那么Bellman方程为:
c ( s , k ) = min ⁡ i ∈ s { c ( s − { i } , i ) + d i , k } c(s,k)=\min_{i \in s}\{c(s-\{i\},i)+d_{i,k}\} c(s,k)=minis{c(s{i},i)+di,k}
为了使用方便,这里使用一个trick,即使用二进制来表达,用位运算符来计算,称作set bits:

  1. 左移和右移运算符可以快速计算2的幂:每左移一位,相当于该数乘以2;每右移一位,相当于该数除以2。因此,1 << k等价于 2 k 2^k 2k。假设S中包含 k 1 , . . . , k n k_1,...,k_n k1,...,kn,则我们可以将s等价替换位 S = 2 k 1 + . . . + 2 k n S=2^{k_1}+...+2^{k_n} S=2k1+...+2kn
  2. 按位或运算符|:可以用来计算集合的并集
  3. 按位与运算符&和取反运算符 ~:可以用来计算集合的差集

我们有初始状态 c ( { 0 } , k ) = ( d 0 , k , 0 ) c(\{0\},k)=(d_{0,k},0) c({0},k)=(d0,k,0)
逐步扩大set的尺寸,遍历所有可能的subset,使用bellman方程迭代计算。我们以n=5为例进行计算,初始状态为:
tsp 动态规划,运筹学,动态规划,算法,机器学习

第一轮结束后,状态清单为:
tsp 动态规划,运筹学,动态规划,算法,机器学习

第三轮结束后,状态清单为:

tsp 动态规划,运筹学,动态规划,算法,机器学习

第四轮结束后,状态清单为:
tsp 动态规划,运筹学,动态规划,算法,机器学习

2. Julia代码示例

using Combinatorics
function held_karp(dists::Array{Int64, 2})
    n = size(dists,1)
    C = Dict{Tuple{Int64, Int64},Tuple{Int64, Int64}}()
    for k in 1:n-1
        C[(1 << k, k)] = (dists[n,k], n)
    end
    for subset_size in 2:n-1
        for subset in combinations(1:n-1,subset_size)
            bits = 0
            for bit in subset;bits |= 1 << bit;end
            for k in subset
                prev = bits & ~(1 << k)
                res =  Array{Tuple{Int64, Int64},1}()
                for m in subset
                    if m == k;continue;end
                    push!(res,(C[(prev,m)][1]+dists[m,k],m))
                end
                C[(bits,k)] = minimum(res)
            end        
        end
    end
    bits = 1<<n - 2
    res = Array{Tuple{Int64, Int64},1}()
    for k in 1:n-1
        push!(res, (C[(bits, k)][1] + dists[k,n], k))
    end
    opt, parent = minimum(res)
    path = []
    for i in 1:n-1
        push!(path,parent)
        new_bits = bits & ~(1 << parent)
        _, parent = C[(bits, parent)]
        bits = new_bits
    end
    path
end

3. python代码示例

import itertools
import random
import sys

def generate_distances(n):
    dists = [[0] * n for i in range(n)]
    for i in range(n):
        for j in range(i+1, n):
            dists[i][j] = dists[j][i] = random.randint(1, 99)
    return dists
    
def held_karp(dists):
    """
    Implementation of Held-Karp, an algorithm that solves the Traveling
    Salesman Problem using dynamic programming with memoization.
    Parameters:
        dists: distance matrix
    Returns:
        A tuple, (cost, path).
    """
    n = len(dists)

    # Maps each subset of the nodes to the cost to reach that subset, as well
    # as what node it passed before reaching this subset.
    # Node subsets are represented as set bits.
    C = {}

    # Set transition cost from initial state
    for k in range(1, n):
        C[(1 << k, k)] = (dists[0][k], 0)

    # Iterate subsets of increasing length and store intermediate results
    # in classic dynamic programming manner
    for subset_size in range(2, n):
        for subset in itertools.combinations(range(1, n), subset_size):
            # Set bits for all nodes in this subset
            bits = 0
            for bit in subset:
                bits |= 1 << bit

            # Find the lowest cost to get to this subset
            for k in subset:
                prev = bits & ~(1 << k)

                res = []
                for m in subset:
                    if m == 0 or m == k: # 不允许回到0,不允许为当前点
                        continue
                    res.append((C[(prev, m)][0] + dists[m][k], m))
                C[(bits, k)] = min(res)

    # We're interested in all bits but the least significant (the start state)
    bits = (2**n - 1) - 1

    # Calculate optimal cost
    res = []
    for k in range(1, n):
        res.append((C[(bits, k)][0] + dists[k][0], k))
    opt, parent = min(res)

    # Backtrack to find full path
    path = []
    for i in range(n - 1):
        path.append(parent)
        new_bits = bits & ~(1 << parent)
        _, parent = C[(bits, parent)]
        bits = new_bits

    # Add implicit start state
    path.append(0)

    return opt, list(reversed(path))

def held_karp2(dists):
    n = len(dists)
    C = {}
    for k in range(1, n):
        C[(1, k)] = (dists[0][k], 0)
    for subset_size in range(1, n):
        for m in range(1, n):
            for subset in itertools.combinations(list(range(1,m))+list(range(m+1,n)), subset_size):
                bits = 1 
                for bit in subset:
                    bits |= 1 << bit
                res = []
                for k in subset: # 决策变量
                    next = bits & ~(1 << k)
                    res.append((C[(next, k)][0] + dists[k][m], k))
                C[(bits, m)] = min(res)

    # We're interested in all bits but the least significant (the start state)
    bits = 2**n - 1

    # Calculate optimal cost
    res = []
    for k in range(1, n):
        res.append((C[(bits & ~(1 << k), k)][0] + dists[k][0], k))
    opt, parent = min(res)

    # Backtrack to find full path
    path = []
    for i in range(n-1):
        path.append(parent)
        bits = bits & ~(1 << parent)
        _, parent = C[(bits, parent)]

    # Add implicit start state
    path.append(0)

    return opt, list(reversed(path))

def held_karp3(dists):
    n = len(dists)
    C = {}
    for k in range(1, n):
        C[(frozenset([k]), k)] = (dists[0][k], 0)
    for subset_size in range(2, n):
        for subset in itertools.combinations(range(1, n), subset_size):
            for k in subset:
                prev = frozenset(set(subset) - {k})
                res = []
                for m in subset:
                    if m == 0 or m == k:
                        continue
                    res.append((C[(prev, m)][0] + dists[m][k], m))
                C[(frozenset(subset), k)] = min(res)

    # We're interested in all bits but the least significant (the start state)
    bits = set(list(range(1,n)))

    # Calculate optimal cost
    res = []
    for k in range(1, n):
        res.append((C[(frozenset(bits), k)][0] + dists[k][0], k))
    opt, parent = min(res)

    # Backtrack to find full path
    path = []
    for i in range(n - 1):
        path.append(parent)
        new_bits = bits - {parent}
        _, parent = C[(frozenset(bits), parent)]
        bits = new_bits

    # Add implicit start state
    path.append(0)

    return opt, list(reversed(path))

简化版如下:文章来源地址https://www.toymoban.com/news/detail-742384.html

# memoization for top down recursion
memo = [[-1]*(1 << (n+1)) for _ in range(n+1)]

def fun(i, mask):
    # base case
    # if only ith bit and 1st bit is set in our mask,
    # it implies we have visited all other nodes already
    if mask == ((1 << i) | 3):
        return dist[1][i]
 
    # memoization
    if memo[i][mask] != -1:
        return memo[i][mask]
 
    res = 10**9  # result of this sub-problem
 
    # we have to travel all nodes j in mask and end the path at ith node
    # so for every node j in mask, recursively calculate cost of
    # travelling all nodes in mask
    # except i and then travel back from node j to node i taking
    # the shortest path take the minimum of all possible j nodes
    for j in range(1, n+1):
        if (mask & (1 << j)) != 0 and j != i and j != 1:
            res = min(res, fun(j, mask & (~(1 << i))) + dist[j][i])
    memo[i][mask] = res  # storing the minimum value
    return res
 
 
# Driver program to test above logic
ans = 10**9
for i in range(1, n+1):
    # try to go from node 1 visiting all nodes in between to i
    # then return from i taking the shortest route to 1
    ans = min(ans, fun(i, (1 << (n+1))-1) + dist[i][1])
 
print("The cost of most efficient tour = " + str(ans))

到了这里,关于运筹系列82:使用动态规划求解TSP问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 强化学习求解TSP:Qlearning求解旅行商问题(Traveling salesman problem, TSP)提供Python代码

    Q-learning是一种强化学习算法,用于解决基于奖励的决策问题。它是一种无模型的学习方法,通过与环境的交互来学习最优策略。Q-learning的核心思想是通过学习一个Q值函数来指导决策,该函数表示在给定状态下采取某个动作所获得的累积奖励。 Q-learning的训练过程如下: 1. 初

    2024年02月03日
    浏览(44)
  • 旅行商问题(TSP)及Python图论求解

    旅行商问题(Traveling Salesman Problem, TSP)是指给定一个城市的集合以及每两个城市之间的距离,找到一条经过每个城市恰好一次且路径最短的回路。 可以想象成一个旅行商要拜访多个城市,问他如何安排路线,使得行程最短。这个问题可能看起来简单,但是随着城市数量的增

    2024年02月12日
    浏览(37)
  • 人工智能导论——遗传算法求解TSP问题实验

    一、实验目的: 熟悉和掌握遗传算法的原理、流程和编码策略,并利用遗传算法求解组合优化问题,理解求解TSP问题的流程并测试主要参数对结果的影响。 二、实验原理: 旅行商问题,即TSP问题(Traveling Salesman Problem)是数学领域中著名问题之一。假设有一个旅行商人要拜

    2023年04月13日
    浏览(46)
  • Hopfield神经网络求解旅行商(TSP)问题matlab代码

            1.网络结构         连续Hopfield神经网络(Continuous Hopfield Neural Network,CHNN)的拓扑结构和离散Hopfield神经网络的结构类似,如图11-1所示。连续Hopfield网络和离散Hopfield 网络的不同点在于其传递函数不是阶跃函数,而是连续函数。         与离散型Hopfield神经网络不

    2024年02月14日
    浏览(42)
  • 人工智能原理实验4(1)——遗传算法、蚁群算法求解TSP问题

    TSP问题是组合数学中一个古老而又困难的问题,也是一个典型的组合优化问题,现已归入NP完备问题类。NP问题用穷举法不能在有效时间内求解,所以只能使用启发式搜索。遗传算法是求解此类问题比较实用、有效的方法之一。下面给出30个城市的位置信息: 应用遗传算法和蚁

    2024年01月24日
    浏览(58)
  • python实现大规模邻域搜索(LNS)求解旅行商问题(TSP)

    参考《Handbook of Metaheuristics (Third Edition)》中的Large neighborhood search章节, 建议直接阅读英文原版 大规模邻域搜索(LNS) 属于超大邻域搜索(Very Large-Scale Neighborhood Search, VLNS)的一类 ,随着算例规模的增大,邻域搜索算法的邻域规模呈指数增长或者当邻域太大而不能在实际中明确搜索

    2024年02月08日
    浏览(42)
  • 97基于matlab的改进的带记忆的模拟退火算法求解TSP问题

    基于matlab的改进的带记忆的模拟退火算法求解TSP问题,采用多普勒型降温曲线描述迭代过程,在传统算法的基础上增加记忆功能,可测试中国31/64/144以及att48城市的数据,也可自行输入数据进行测试,测试结果基本达到当前最优水平。duoci.m为主文件。数据可更换自己的,程序

    2024年02月05日
    浏览(53)
  • 【运筹优化】子集和问题(Subset Sum Problems , SSP)介绍 + 动态规划求解 + Java代码实现

    子集和问题(Subset Sum Problems , SSP),它是复杂性理论中最重要的问题之一。 SSP会给定一组整数 a 1 , a 2 , . . . . , a n a_1,a_2,....,a_n a 1 ​ , a 2 ​ , .... , a n ​ ,最多 n n n 个整数,我们需要判断是否存在一个非空子集,使得子集的总和为 M M M 整数?如果存在则需要输出该子集。

    2024年01月17日
    浏览(50)
  • 运筹说 第67期 | 动态规划模型的建立与求解

    通过前一期的学习,我们已经学会了动态规划的基本概念和基本原理。本期小编带大家学习动态规划模型的建立与求解。 动态规划模型的建立 一   概述 建立动态规划的模型,就是分析问题并建立问题的动态规划基本方程。 成功地应用动态规划方法的关键,在于识别问题的

    2024年01月16日
    浏览(36)
  • python调用SCIP求解TSP(callback方式实现消除子环路subtour)

    callback解决方案 The constraints (3) exclude subtours by imposing that for any proper subset S of the vertex set V such that |S| ≥ 2 a solution cannot encompass a cycle within S. However, as there is an exponential number of subsets of V , it is impractical to specify all of these constraints. A possible approach is to iteratively solve the problem, st

    2024年02月06日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包