修改Held-Karp TSP算法,这样我们就不需要回到原点了

时间:2017-04-14 13:37:47

标签: php python algorithm

我必须解决一个问题,我必须找到从距离矩阵开始链接所有点的最短路径。它几乎就像一个旅行推销员问题,除了我不需要通过返回起点来关闭我的路径。我发现Held-Karp algorithm(Python)很好地解决了TSP,但总是计算返回起点的距离。所以现在它给我留下了3个问题:

  1. 如果我修改我的功能不回到起点,至少有一种情况会有不同的结果吗?
  2. 如果1的答案是肯定的,我怎么能改变我的hold_karp()函数以满足我的需要呢?
  3. 2中没有办法,我接下来要找什么?
  4. 我已将held_karp()函数从Python翻译为PHP,对于我的解决方案,我很乐意使用这两种语言。

    function held_karp($matrix) {
        $nb_nodes = count($matrix);
    
        # 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 = 1; $k < $nb_nodes; $k++) $c["(".(1 << $k).",$k)"] = [$matrix[0][$k], 0];
    
        # Iterate subsets of increasing length and store intermediate results
        # in classic dynamic programming manner
        for($subset_size = 2; $subset_size < $nb_nodes; $subset_size++) {
            $combinaisons = every_combinations(range(1, $nb_nodes - 1), $subset_size, false);
            foreach($combinaisons AS $subset) {
                # Set bits for all nodes in this subset
                $bits = 0;
                foreach($subset AS $bit) $bits |= 1 << $bit;
    
                # Find the lowest cost to get to this subset
                foreach($subset AS $bk) {
                    $prev = $bits & ~(1 << $bk);
    
                    $res = [];
                    foreach($subset AS $m) {
                        if(($m == 0)||($m == $bk)) continue;
                        $res[] = [$c["($prev,$m)"][0] + $matrix[$m][$bk], $m];
                    }
                    $c["($bits,$bk)"] = min($res);
                }
            }
        }
    
        # We're interested in all bits but the least significant (the start state)
        $bits = (2**$nb_nodes - 1) - 1;
    
        # Calculate optimal cost
        $res = [];
        for($k = 1; $k < $nb_nodes; $k++) $res[] = [$c["($bits,$k)"][0] + $matrix[$k][0], $k];
        list($opt, $parent) = min($res);
    
        # Backtrack to find full path
        $path = [];
        for($i = 0; $i < $nb_nodes - 1; $i++) {
            $path[] = $parent;
            $new_bits = $bits & ~(1 << $parent);
            list($scrap, $parent) = $c["($bits,$parent)"];
            $bits = $new_bits;
        }
    
        # Add implicit start state
        $path[] = 0;
    
        return [$opt, array_reverse($path)];
    }
    

    如果您需要知道every_combinations()函数的工作原理

    function every_combinations($set, $n = NULL, $order_matters = true) {
        if($n == NULL) $n = count($set);
        $combinations = [];
        foreach($set AS $k => $e) {
            $subset = $set;
            unset($subset[$k]);
            if($n == 1) $combinations[] = [$e];
            else {
                $subcomb = every_combinations($subset, $n - 1, $order_matters);
                foreach($subcomb AS $s) {
                    $comb = array_merge([$e], $s);
                    if($order_matters) $combinations[] = $comb;
                    else {
                        $needle = $comb;
                        sort($needle);
                        if(!in_array($needle, $combinations)) $combinations[] = $comb;
                    }
                }
            }
        }
        return $combinations;
    }
    

1 个答案:

答案 0 :(得分:1)

是的,答案可能不同。例如,如果图形有4个顶点和下面的无向边:

1-2 1
2-3 1
3-4 1
1-4 100
1-3 2
2-4 2

最佳路径为1-2-3-4,权重为1 + 1 + 1 = 3,但同一周期的权重为1 + 1 + 1 + 100 = 103.但是,周期的权重为{{ 1}}为2 + 1 + 2 + 1 = 6且该路径的权重为2 + 1 + 2 = 5,因此最优周期和最优路径不同。

如果你正在寻找路径而不是循环,你可以使用相同的算法,但是你不需要将最后一条边的权重加到起始顶点,即

1-3-4-2

应为for($k = 1; $k < $nb_nodes; $k++) $res[] = [$c["($bits,$k)"][0] + $matrix[$k][0], $k];