首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >关于加速解决旅行商问题的动态规划的建议?

关于加速解决旅行商问题的动态规划的建议?
EN

Stack Overflow用户
提问于 2017-09-20 09:00:33
回答 1查看 1.3K关注 0票数 2

我正在学习一个在线课程,其中一个任务是实现一个动态规划算法来解决旅行推销员问题(TSP)。我的Python实现适用于小型案例(~5个城市),但对于25个城市的“实际”应用程序来说,它似乎非常慢。我正在寻找加速算法的建议。

该算法在以下摘录中进行了描述:

http://www.geeksforgeeks.org/travelling-salesman-problem-set-1/上也描述了动态规划的解决方案,并给出了其他参考。

任务的问题陈述是:

我使用一个用于数组DataFrame的熊猫A对象实现了伪代码。由于集合是不可理解的,不能用作索引,所以我使用元组,注意排序,以便使它们成为集合的唯一表示形式。下面是代码以及几个越来越大的测试用例:

代码语言:javascript
复制
import functools
from itertools import combinations
import numpy as np
import pandas as pd
from cached_property import cached_property
import pytest


def powerset_list(s):
    '''Return a list of tuples representing all subsets of s'''
    return functools.reduce(lambda x, y: x + y, [list(combinations(s, r)) for r in range(len(s)+1)])


class Graph(object):
    def __init__(self, edges):
        self.edges = edges

    @cached_property
    def nodes(self):
        _nodes = set()
        for edge in self.edges:
            u, v, weight = edge
            _nodes.add(u)
            _nodes.add(v)
        return list(_nodes)

    @cached_property
    def W(self):
        '''Matrix of edge weights'''
        n = len(self.nodes)
        w = np.full((n, n), np.inf)
        np.fill_diagonal(w, 0)
        w = pd.DataFrame(w, index=range(1, n+1), columns=range(1, n+1))
        for edge in self.edges:
            u, v, weight = edge
            w.set_value(u, v, weight)
            w.set_value(v, u, weight)
        return w

    def tsp(self):
        '''Solve the traveling salesman problem using a dynamic programming method'''
        n = len(self.nodes)
        sets = [(1,) + x for x in powerset_list(range(2, n+1))]
        A = pd.DataFrame(np.full((len(sets), n), np.inf), index=sets, columns=range(1, n+1))
        A.set_value((1,), 1, 0)
        for m in range(2, n+1):
            for S in [(1,) + perm for perm in combinations(range(2, n+1), m-1)]:
                for j in set(S) - set([1]):
                    S_min_j = tuple(sorted(set(S) - set([j])))
                    A.set_value(S, j, min(A.get_value(S_min_j, k) + self.W.get_value(k, j) for k in S_min_j))
        return min(A.get_value(tuple(range(1, n+1)), j) + self.W.get_value(j, 1) for j in range(2, n+1))


@pytest.fixture
def edges_geeksforgeeks():
    '''Edges from the example graph on http://www.geeksforgeeks.org/travelling-salesman-problem-set-1/'''
    return [(1, 2, 10), (1, 3, 15), (1, 4, 20), (2, 3, 35), (2, 4, 25), (3, 4, 30)]

def test_tsp(edges_geeksforgeeks):
    graph = Graph(edges_geeksforgeeks)
    min_cost = graph.tsp()
    assert min_cost == 80


def dist(coord1, coord2):
    return np.linalg.norm(np.array(coord1) - np.array(coord2))

def edges_from_coords(filename):
    with open(filename) as f:
        coords = [tuple(map(float, line.split())) for line in f.read().splitlines()[1:]]
    nodes = list(range(1, len(coords) + 1))
    coords = dict(zip(nodes, coords))
    return [(comb[0], comb[1], dist(coords[comb[0]], coords[comb[1]])) for comb in combinations(nodes, 2)]

@pytest.mark.parametrize("test_input, expected", [("Hulburd_1.txt", 10.24), ("Hulburd_2.txt", 12.36), ("Hulburd_3.txt", 14.00)])
def test_Hulburd(test_input, expected):
    '''Test data supplied by Eric Hulburd on the course forum'''
    edges = edges_from_coords(test_input)
    graph = Graph(edges)
    min_cost = graph.tsp()
    assert np.around(min_cost, decimals=2) == expected

@pytest.fixture
def edges_cities():
    return edges_from_coords('tsp.txt')

@pytest.mark.skip(reason="This takes too long to run")
def test_tsp_cities(edges_cities):
    graph = Graph(edges_cities)
    min_cost = graph.tsp()
    print("The minimum cost rounded down to the nearest integer is {}".format(int(np.floor(min_cost))))


if __name__ == "__main__":
    pytest.main([__file__, "-s"])

测试中使用的文件是1.txt2.txt3.txt和实际赋值的主文件tsp.txt。问题是,涉及tsp.txt的最后一个(跳过的)测试运行时间太长。

我该如何加快算法的速度?在课程论坛上,有人说他们用位掩码和并行化让它在3分钟内运行;另一个建议是简化数组的索引,而不是使用元组。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-09-20 21:55:36

提高绩效的几点建议:

  • 不要使用元组,而是使用32位ints来表示您的子集--如果您的城市不超过32个,这就足够了。
  • 在每一步中,您只需要对大小为m-1的子集进行以前的计算值(您不需要存储任何大小为m-2、m-3等的子集的值)--这可能会大大减少您的内存使用量。
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46317631

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档