繁体   English   中英

ChocoSolver - 找到访问所有城市的最少路径

[英]ChocoSolver - Find the minimal number of path to visit all cities

我在 Java 中使用 Choco Solver 4.10.8 来解决约束编程问题。 目标是找到我们需要用来访问矩阵 M 中的所有城市的最小路径数,矩阵 M 在这里定义。
我们有 4 条路径和 6 个城市。

static int[][] M = new int[][]{     
        {2, 3, 4, 5, 6},
        {1, 2, 5},
        {2, 3, 4},      
        {3, 4, 6}                     
};

在这里,求解器应该返回 0 和 1,它们是我们查看所有 6 个城市所需的路径的索引。 事实上,只有 2 条路径,我们已经到达了矩阵中的所有城市。

我是 Choco Solver 的初学者,在定义问题的约束时遇到了麻烦。 我认为我已经很好地定义了问题的变量,这里有:

Model model = new Model("minimalPaths");

// VARIABLES

int nbPath = M.length;
int nbCity = 6;

// Table of nbPath length which shows if a path is used (1) or no (0)
IntVar[] usedPath = model.intVarArray("path", nbPath, 0, 1);

// Which paths are used by each city
IntVar[][] city_by_path = model.intVarMatrix("city", nbCity, nbPath,  0, 1);

// Total number of paths used for the solution - we want to minimize that
IntVar totalPath = model.intVar("totalPath", 0, nbPath);        

我选择使用 IntVar 而不是 BoolVar,因为我认为处理不同的约束更容易。 关于约束,我试过这个:

// CONTRAINTES
        
// We loop over the paths of M
for(int i=0; i<nbPath; i++) {
    // We loop over all the cities for a Path
    int[] path = M[i];
    for(int city: path) {
        // If the Path is used or not, we update the value in the table
        model.element(usedPath[i], city_by_path[city-1], model.intVar(i), 0).post();
    }
}

// This constraint is used to have at least one path used for one city
// This means we want to see all the cities
for(int i = 0; i<nbCity; i++) {
    model.sum(city_by_path[i], ">=", model.intVar(1)).post();
}
        
// The sum of all the Path used is the variable we will minimize
// usedPath[i] = 0 or 1 so if we sum all our value, we will have the total number of paths used
model.sum(usedPath, "=", totalPath).post();
    
// We want to minimize the total number of Paths we will use
model.setObjective(Model.MINIMIZE, totalPath);

我确信我的一些约束定义不好,但我真的不知道在哪里。
当我尝试解决这个问题时,我打印了与解决方案关联的对象 city_by_path。 它向我表明解决方案不好,并且不尊重约束......

    path i=2 is used
City 1: 1 0 0 0 
City 2: 0 0 1 0 
City 3: 0 0 1 0 
City 4: 0 0 1 0 
City 5: 0 0 0 1 
City 6: 0 1 0 0 
    Total path used : 1

我想知道您是否可以查看我的代码并就我的变量定义和此问题的约束给我一些建议。

实际上,您不需要将 city_by_path 变量声明为新变量。 实际上,要么使用路径并访问其城市,要么不使用路径并且矩阵中的所有城市都将被赋值为零(对于不在路径中的城市总是如此)。

为了使它工作,你可以声明你的矩阵如下:

IntVar[][] city_by_path = new IntVar[nbCity][nbPath];

并且,无论城市是否在路径中,都使用 usedPath 变量或值为零的变量填充矩阵,而不是元素约束:

for (int p = 0; p < nbPath; p++) {
    for (int c = 0; c < nbCity; c++) {
        final int cityId = c + 1;
        if (Arrays.stream(M[p]).anyMatch(i -> i == cityId)) {
            city_by_path[c][p] = usedPath[p];
        } else {
            city_by_path[c][p] = model.intVar(0);
        }
    }
}

您的代码的其余部分是正确的。 此外,在使用约束求解器求解 CSP 时,通过指定要分支的变量来帮助求解器始终是一个好主意。 专门针对问题设计的分支方案通常能更好地快速找到解决方案。 在这里,您可以添加以下分支方案(这是 3 个节点中的最佳解决方案并且没有任何回溯 - 特别是因为问题的实例非常简单):

model.getSolver().setSearch(Search.inputOrderLBSearch(usedPath));

在不涉及 Choco 的细节的情况下,您描述的问题通常称为Set Covering 问题 有关离散优化课程中作业上下文中问题的一些评论,请参阅此视频

对此进行建模的一种简单方法是为每个路径设置一个布尔变量,指示是否选择了它。 然后,对于每个城市,您添加一个约束,即至少选择访问该城市的一条路径。 这可以通过析取(访问当前城市的路径的布尔变量或布尔变量)或通过要求相关路径的总和至少为一个来完成。

换句话说,每个路径只需要一个变量。 无需找出访问城市的路径。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM