用Python定义旅行推销员的线性规划模型

用Python定义旅行推销员的线性规划模型,python,algorithm,python-2.7,linear-programming,pulp,Python,Algorithm,Python 2.7,Linear Programming,Pulp,利用Python和pill库,我们如何创建线性规划模型来解决旅行商问题(TSP) 在维基百科中,目标函数和约束条件是 问题:这是我的部分尝试,我被卡住了 我没有在代码中包含最终约束,因为我不知道如何定义它。我相信u变量的约束是为了防止解决方案中出现子循环 此外,对当前模型的求解给出了决策变量,如x0_0和x1_1等于1.0,这肯定是错误的。。。我不明白为什么会这样即使我有 if i == j: upperBound = 0 Python代码 import pulp

利用Python和
pill
库,我们如何创建线性规划模型来解决旅行商问题(TSP)

在维基百科中,目标函数和约束条件是

问题:这是我的部分尝试,我被卡住了

  • 我没有在代码中包含最终约束,因为我不知道如何定义它。我相信u变量的约束是为了防止解决方案中出现子循环

  • 此外,对当前模型的求解给出了决策变量,如
    x0_0
    x1_1
    等于
    1.0
    ,这肯定是错误的。。。我不明白为什么会这样即使我有

        if i == j:
            upperBound = 0
    
  • Python代码

    import pulp
    
    def get_dist(tsp):
        with open(tsp, 'rb') as tspfile:
            r = csv.reader(tspfile, delimiter='\t')
            d = [row for row in r]
    
        d = d[1:] # skip header row
        locs = set([r[0] for r in d]) | set([r[1] for r in d])
        loc_map = {l:i for i, l in enumerate(locs)}
        idx_map = {i:l for i, l in enumerate(locs)}
        dist = [(loc_map[r[0]], loc_map[r[1]], r[2]) for r in d]
        return dist, idx_map
    
    
    def dist_from_coords(dist, n):
        points = []
        for i in range(n):
            points.append([0] * n)
        for i, j, v in dist:
            points[i][j] = points[j][i] = float(v)
        return points
    
    
    def find_tour():
        tsp_file = '/Users/test/' + 'my-waypoints-dist-dur.tsv'
        coords, idx_map = get_dist(tsp_file)
        n = len(idx_map)
        dist = dist_from_coords(coords, n)
    
    
        # Define the problem
        m = pulp.LpProblem('TSP', pulp.LpMinimize)
    
    
        # Create variables
        # x[i,j] is 1 if edge i->j is on the optimal tour, and 0 otherwise
        # Also forbid loops
        x = {}
        for i in range(n):
            for j in range(n):
                lowerBound = 0
                upperBound = 1
    
                # Forbid loops
                if i == j:
                    upperBound = 0
                    # print i,i
    
                x[i,j] = pulp.LpVariable('x' + str(i) + '_' + str(j), lowerBound, upperBound, pulp.LpBinary)
                # x[j,i] = x[i,j]
    
    
        # Define the objective function to minimize
        m += pulp.lpSum([dist[i][j] * x[i,j] for i in range(n) for j in range(n)])
    
    
        # Add degree-2 constraint
        for i in range(n):
            m += pulp.lpSum([x[i,j] for j in range(n)]) == 2
    
    
        # Solve and display results
        status = m.solve()
        print pulp.LpStatus[status]
        for i in range(n):
            for j in range(n):
                if pulp.value(x[i,j]) >0:
                    print str(i) + '_' + str(j) + ': ' + str( pulp.value(x[i,j]) )
    
    find_tour()
    
    import csv
    import pulp
    
    
    def get_dist(tsp):
        with open(tsp, 'rb') as tspfile:
            r = csv.reader(tspfile, delimiter='\t')
            d = [row for row in r]
    
        d = d[1:] # skip header row
        locs = set([r[0] for r in d]) | set([r[1] for r in d])
        loc_map = {l:i for i, l in enumerate(locs)}
        idx_map = {i:l for i, l in enumerate(locs)}
        dist = [(loc_map[r[0]], loc_map[r[1]], r[2]) for r in d]
        return dist, idx_map
    
    
    def dist_from_coords(dist, n):
        points = []
        for i in range(n):
            points.append([0] * n)
        for i, j, v in dist:
            points[i][j] = points[j][i] = float(v)
        return points
    
    
    def find_tour():
        tsp_file = '/Users/test/' + 'my-waypoints-dist-dur.tsv'
        coords, idx_map = get_dist(tsp_file)
        n = len(idx_map)
        dist = dist_from_coords(coords, n)
    
    
        # Define the problem
        m = pulp.LpProblem('TSP', pulp.LpMinimize)
    
    
        # Create variables
        # x[i,j] is 1 if edge i->j is on the optimal tour, and 0 otherwise
        # Also forbid loops
        x = {}
        for i in range(n+1):
            for j in range(n+1):
                lowerBound = 0
                upperBound = 1
    
                # Forbid loops
                if i == j:
                    upperBound = 0
                    # print i,i
    
                # Create the decision variable and First constraint
                x[i,j] = pulp.LpVariable('x' + str(i) + '_' + str(j), lowerBound, upperBound, pulp.LpBinary)
                # x[j,i] = x[i,j]
    
    
        # Define the objective function to minimize
        m += pulp.lpSum([dist[i][j] * x[i,j] for i in range(1,n+1) for j in range(1,n+1)])
    
    
        # Add degree-2 constraint (3rd and 4th)
        for i in range(1,n+1):
            m += pulp.lpSum([x[i,j] for j in range(1,n+1)]) == 2
    
    
        # Add the last (5th) constraint (prevents subtours)
        u = []
        for i in range(1, n+1):
            u.append(pulp.LpVariable('u_' + str(i), cat='Integer'))
        for i in range(1, n-1):
            for j in range(i+1, n+1):
                m += pulp.lpSum([ u[i] - u[j] + n*x[i,j]]) <= n-1
    
    
        # status = m.solve()
        # print pulp.LpStatus[status]
        # for i in range(n):
        #   for j in range(n):
        #       if pulp.value(x[i,j]) >0:
        #           print str(i) + '_' + str(j) + ': ' + str( pulp.value(x[i,j]) )
    
    find_tour()
    
    tsv期间我的航路点距离

    数据文件

    结果

    0_0: 1.0
    0_5: 1.0
    1_1: 1.0
    1_15: 1.0
    2_2: 1.0
    2_39: 1.0
    3_3: 1.0
    3_26: 1.0
    4_4: 1.0
    4_42: 1.0
    5_5: 1.0
    5_33: 1.0
    6_6: 1.0
    6_31: 1.0
    7_7: 1.0
    7_38: 1.0
    8_8: 1.0
    8_24: 1.0
    9_9: 1.0
    9_26: 1.0
    10_4: 1.0
    10_10: 1.0
    11_11: 1.0
    11_12: 1.0
    12_11: 1.0
    12_12: 1.0
    13_13: 1.0
    13_17: 1.0
    14_14: 1.0
    14_18: 1.0
    15_1: 1.0
    15_15: 1.0
    16_3: 1.0
    16_16: 1.0
    17_13: 1.0
    17_17: 1.0
    18_14: 1.0
    18_18: 1.0
    19_19: 1.0
    19_20: 1.0
    20_4: 1.0
    20_20: 1.0
    21_21: 1.0
    21_25: 1.0
    22_22: 1.0
    22_27: 1.0
    23_21: 1.0
    23_23: 1.0
    24_8: 1.0
    24_24: 1.0
    25_21: 1.0
    25_25: 1.0
    26_26: 1.0
    26_43: 1.0
    27_27: 1.0
    27_38: 1.0
    28_28: 1.0
    28_47: 1.0
    29_29: 1.0
    29_31: 1.0
    30_30: 1.0
    30_34: 1.0
    31_29: 1.0
    31_31: 1.0
    32_25: 1.0
    32_32: 1.0
    33_28: 1.0
    33_33: 1.0
    34_30: 1.0
    34_34: 1.0
    35_35: 1.0
    35_42: 1.0
    36_36: 1.0
    36_47: 1.0
    37_36: 1.0
    37_37: 1.0
    38_27: 1.0
    38_38: 1.0
    39_39: 1.0
    39_44: 1.0
    40_40: 1.0
    40_43: 1.0
    41_41: 1.0
    41_45: 1.0
    42_4: 1.0
    42_42: 1.0
    43_26: 1.0
    43_43: 1.0
    44_39: 1.0
    44_44: 1.0
    45_15: 1.0
    45_45: 1.0
    46_40: 1.0
    46_46: 1.0
    47_28: 1.0
    47_47: 1.0
    
    
    
    ...
    

    更新代码

    import pulp
    
    def get_dist(tsp):
        with open(tsp, 'rb') as tspfile:
            r = csv.reader(tspfile, delimiter='\t')
            d = [row for row in r]
    
        d = d[1:] # skip header row
        locs = set([r[0] for r in d]) | set([r[1] for r in d])
        loc_map = {l:i for i, l in enumerate(locs)}
        idx_map = {i:l for i, l in enumerate(locs)}
        dist = [(loc_map[r[0]], loc_map[r[1]], r[2]) for r in d]
        return dist, idx_map
    
    
    def dist_from_coords(dist, n):
        points = []
        for i in range(n):
            points.append([0] * n)
        for i, j, v in dist:
            points[i][j] = points[j][i] = float(v)
        return points
    
    
    def find_tour():
        tsp_file = '/Users/test/' + 'my-waypoints-dist-dur.tsv'
        coords, idx_map = get_dist(tsp_file)
        n = len(idx_map)
        dist = dist_from_coords(coords, n)
    
    
        # Define the problem
        m = pulp.LpProblem('TSP', pulp.LpMinimize)
    
    
        # Create variables
        # x[i,j] is 1 if edge i->j is on the optimal tour, and 0 otherwise
        # Also forbid loops
        x = {}
        for i in range(n):
            for j in range(n):
                lowerBound = 0
                upperBound = 1
    
                # Forbid loops
                if i == j:
                    upperBound = 0
                    # print i,i
    
                x[i,j] = pulp.LpVariable('x' + str(i) + '_' + str(j), lowerBound, upperBound, pulp.LpBinary)
                # x[j,i] = x[i,j]
    
    
        # Define the objective function to minimize
        m += pulp.lpSum([dist[i][j] * x[i,j] for i in range(n) for j in range(n)])
    
    
        # Add degree-2 constraint
        for i in range(n):
            m += pulp.lpSum([x[i,j] for j in range(n)]) == 2
    
    
        # Solve and display results
        status = m.solve()
        print pulp.LpStatus[status]
        for i in range(n):
            for j in range(n):
                if pulp.value(x[i,j]) >0:
                    print str(i) + '_' + str(j) + ': ' + str( pulp.value(x[i,j]) )
    
    find_tour()
    
    import csv
    import pulp
    
    
    def get_dist(tsp):
        with open(tsp, 'rb') as tspfile:
            r = csv.reader(tspfile, delimiter='\t')
            d = [row for row in r]
    
        d = d[1:] # skip header row
        locs = set([r[0] for r in d]) | set([r[1] for r in d])
        loc_map = {l:i for i, l in enumerate(locs)}
        idx_map = {i:l for i, l in enumerate(locs)}
        dist = [(loc_map[r[0]], loc_map[r[1]], r[2]) for r in d]
        return dist, idx_map
    
    
    def dist_from_coords(dist, n):
        points = []
        for i in range(n):
            points.append([0] * n)
        for i, j, v in dist:
            points[i][j] = points[j][i] = float(v)
        return points
    
    
    def find_tour():
        tsp_file = '/Users/test/' + 'my-waypoints-dist-dur.tsv'
        coords, idx_map = get_dist(tsp_file)
        n = len(idx_map)
        dist = dist_from_coords(coords, n)
    
    
        # Define the problem
        m = pulp.LpProblem('TSP', pulp.LpMinimize)
    
    
        # Create variables
        # x[i,j] is 1 if edge i->j is on the optimal tour, and 0 otherwise
        # Also forbid loops
        x = {}
        for i in range(n+1):
            for j in range(n+1):
                lowerBound = 0
                upperBound = 1
    
                # Forbid loops
                if i == j:
                    upperBound = 0
                    # print i,i
    
                # Create the decision variable and First constraint
                x[i,j] = pulp.LpVariable('x' + str(i) + '_' + str(j), lowerBound, upperBound, pulp.LpBinary)
                # x[j,i] = x[i,j]
    
    
        # Define the objective function to minimize
        m += pulp.lpSum([dist[i][j] * x[i,j] for i in range(1,n+1) for j in range(1,n+1)])
    
    
        # Add degree-2 constraint (3rd and 4th)
        for i in range(1,n+1):
            m += pulp.lpSum([x[i,j] for j in range(1,n+1)]) == 2
    
    
        # Add the last (5th) constraint (prevents subtours)
        u = []
        for i in range(1, n+1):
            u.append(pulp.LpVariable('u_' + str(i), cat='Integer'))
        for i in range(1, n-1):
            for j in range(i+1, n+1):
                m += pulp.lpSum([ u[i] - u[j] + n*x[i,j]]) <= n-1
    
    
        # status = m.solve()
        # print pulp.LpStatus[status]
        # for i in range(n):
        #   for j in range(n):
        #       if pulp.value(x[i,j]) >0:
        #           print str(i) + '_' + str(j) + ': ' + str( pulp.value(x[i,j]) )
    
    find_tour()
    
    导入csv
    进口纸浆
    def get_dist(tsp):
    打开(tsp,'rb')作为tsp文件:
    r=csv.reader(tspfile,分隔符='\t')
    d=[r中的行对行]
    d=d[1:]#跳过标题行
    locs=集合([r[0]表示d中的r])|集合([r[1]表示d中的r])
    loc_map={l:i代表i,l在枚举(locs)}
    idx_-map={i:l代表i,l在枚举(locs)}
    对于d中的r,dist=[(loc_map[r[0]],loc_map[r[1]],r[2])]
    返回距离,idx\U映射
    def dist_from_coords(北区):
    点数=[]
    对于范围(n)中的i:
    点。追加([0]*n)
    对于距离中的i、j、v:
    点[i][j]=点[j][i]=浮动(v)
    返回点
    def find_tour():
    tsp_file='/Users/test/'+'my waypoints dist dur.tsv'
    coords,idx\u map=get\u dist(tsp\u文件)
    n=len(idx_映射)
    距离=距离坐标的距离(坐标,n)
    #定义问题
    m=纸浆.lp问题('TSP',纸浆.lp最小化)
    #创建变量
    #如果边i->j处于最佳行程,则x[i,j]为1,否则为0
    #也禁止循环
    x={}
    对于范围(n+1)内的i:
    对于范围(n+1)内的j:
    lowerBound=0
    上限=1
    #禁止循环
    如果i==j:
    上限=0
    #打印i,i
    #创建决策变量和第一个约束
    x[i,j]=palp.LpVariable('x'+str(i)+'.'+str(j),下界,上界,palp.LpBinary)
    #x[j,i]=x[i,j]
    #定义最小化的目标函数
    m+=纸浆lpSum([dist[i][j]*x[i,j]表示范围(1,n+1)内的i,表示范围(1,n+1)内的j)])
    #添加阶数为2的约束(第3和第4)
    对于范围(1,n+1)内的i:
    m+=纸浆.lpSum([x[i,j]表示范围(1,n+1)内的j)]==2
    #添加最后一个(第5个)约束(防止子任务)
    u=[]
    对于范围(1,n+1)内的i:
    u、 追加(pulp.LpVariable('u_'+str(i),cat='Integer'))
    对于范围(1,n-1)内的i:
    对于范围(i+1,n+1)内的j:
    m+=纸浆.lpSum([u[i]-u[j]+n*x[i,j]])0:
    #打印str(i)+'''.'+str(j)+':'+str(纸浆值(x[i,j]))
    查找_tour()
    
    最后一个约束不是单个约束。您应该为满足该条件的每对索引
    i,j
    添加一个约束:

    for i in range(n-1):
        for j in range(i+1, n):
            m += pulp.lpSum([ u[i] - u[j] + n*x[i,j]]) <= n-1
    

    你的代码有什么问题?@kindall我没有包括最后的约束,因为我不知道如何定义它。此外,对当前模型的求解给出了决策变量,如
    x1_1
    等于
    1.0
    ,这肯定是错误的。。。我不明白为什么会这样。@kindall更新了问题以澄清问题,并在当前代码中包含了不正确的结果。您将无法使用此方法解决更大的问题,因为由于子巡更消除约束,公式将变得非常大。您可以考虑另一种动态分离无效解决方案的方法。这里有一个Python示例:@mattmilten感谢您的建议,非常有用,因为我对TSP和LP还不熟悉,不知道什么方法更有效,特别是在现实生活中使用的方法。谢谢,我正在研究您的建议。到目前为止,当前代码看起来正常吗?缺少最后一个约束是不是因为
    x0_0
    x1_1
    等被解决为具有
    1.0
    的值而不是
    0.0
    ?@Nyxynyx最后一个约束对于问题来说是绝对基本的。没有它,问题甚至不是一个整数线性问题,而只是一个线性问题,因此它不能以任何有意义的方式表示TSP(请记住,TSP不能在多项式时间内求解,甚至不能在多项式时间内以“好的方式”近似,因此每个非整数线性规划问题都是如此)(总是可以在多项式时间内求解)不能表示它或任何一种良好的近似)当我试图同化最后一个约束的代码时,我将它们添加到当前代码并尝试求解它,但结果仍然很奇怪,变量
    x[I,j]的值为
    1.0
    其中
    i==j
    @Nyxynyx我相信您的代码中可能有错误。在原始模型中,如果我没有弄错的话,索引为
    0
    的节点应该是“关闭循环”的节点。您可以看到,在目标函数中,您有从
    i=0
    n
    的外部和。但是,其他约束从
    i=1
    开始。在您的代码中,似乎您认为模型只是
    1
    -索引,因此将约束更改为
    0
    -索引…@Nyxynyx您应该尝试定义您的
    x
    变量使用范围(n+1)中的i的
    :范围(n+1)中的j的
    ,并修改所有约束以从
    1开始,因此即使最后一个约束也应该是范围(1,n)中的i的
    :范围(i+1,n+1)中的j的
    :…
    。此外,没有
    u\u 0
    变量,因此在定义
    u
    向量时,您必须对此进行调整…只需添加一个伪en