简体   繁体   English

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

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

I am using Choco Solver 4.10.8 in Java for a constraint programming problem.我在 Java 中使用 Choco Solver 4.10.8 来解决约束编程问题。 The goal is to find the minimal number of path we need to use to visit all the cities in a matrix M which is defined here.目标是找到我们需要用来访问矩阵 M 中的所有城市的最小路径数,矩阵 M 在这里定义。
We have 4 paths, and 6 cities.我们有 4 条路径和 6 个城市。

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

Here the solver should return 0 and 1, which are the indexes of the path we need to take to see all the 6 cities.在这里,求解器应该返回 0 和 1,它们是我们查看所有 6 个城市所需的路径的索引。 Indeed, with 2 paths only, we have reached all the cities in the matrix.事实上,只有 2 条路径,我们已经到达了矩阵中的所有城市。

I'm a beginner with Choco Solver, and I'm having trouble defining the constraints of the problem.我是 Choco Solver 的初学者,在定义问题的约束时遇到了麻烦。 I think that I have well defined the variables of the problem, here are there:我认为我已经很好地定义了问题的变量,这里有:

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);        

I chose to use IntVar and not BoolVar because I think that it's easier to deal with the different constraints.我选择使用 IntVar 而不是 BoolVar,因为我认为处理不同的约束更容易。 Concerning the constraints, I have tried this:关于约束,我试过这个:

// 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);

I'm sure that some of my constraints are badly defined but I really don't know where.我确信我的一些约束定义不好,但我真的不知道在哪里。
When I try to solve this, I print the object city_by_path associated to the solution.当我尝试解决这个问题时,我打印了与解决方案关联的对象 city_by_path。 It shows me that the solution is not good and the constraints are not respected...它向我表明解决方案不好,并且不尊重约束......

    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

I was wondering if you can have a look to my code and give me some advices about the definition of my variables and constraints for this problem.我想知道您是否可以查看我的代码并就我的变量定义和此问题的约束给我一些建议。

Actually, you don't need to declare the city_by_path variables as new ones.实际上,您不需要将 city_by_path 变量声明为新变量。 Indeed, either the path is used and its cities are visited, either the path is not used and all cities will be valued to zero in the matrix (which is always true for cities not in the path).实际上,要么使用路径并访问其城市,要么不使用路径并且矩阵中的所有城市都将被赋值为零(对于不在路径中的城市总是如此)。

To make it work, you can declare your matrix the following:为了使它工作,你可以声明你的矩阵如下:

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

And, instead of your element constraints, you fill the matrix with the usedPath variables or with variables valued to zero whether the city is in the path or not:并且,无论城市是否在路径中,都使用 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);
        }
    }
}

The remainder of your code is correct.您的代码的其余部分是正确的。 Also, when solving a CSP with a constraint solver, it is always a good idea to help the solver by specifying on which variables to branch.此外,在使用约束求解器求解 CSP 时,通过指定要分支的变量来帮助求解器始终是一个好主意。 Branching scheme designed specifically for the problem are generally better to find solutions quickly.专门针对问题设计的分支方案通常能更好地快速找到解决方案。 Here, you can add the following branching scheme (which an optimal solution in 3 nodes and without any backtrack - especially because the instance of the problem is very easy):在这里,您可以添加以下分支方案(这是 3 个节点中的最佳解决方案并且没有任何回溯 - 特别是因为问题的实例非常简单):

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

Without going into the specifics of Choco, the problem you describe is more commonly known as the Set Covering problem .在不涉及 Choco 的细节的情况下,您描述的问题通常称为Set Covering 问题 For some comments on the problem in the context of an assignment in a discrete optimization course see this video .有关离散优化课程中作业上下文中问题的一些评论,请参阅此视频

A simple way to model this is to have a Boolean variable for each of your paths indicating if it is chosen or not.对此进行建模的一种简单方法是为每个路径设置一个布尔变量,指示是否选择了它。 Then for each city, you add a constraint that at least one of the paths that visit that city is chosen.然后,对于每个城市,您添加一个约束,即至少选择访问该城市的一条路径。 This can be done either with disjunction (an or over the Boolean variables for the paths visiting the current city) or by requiring that the sum of the relevant paths is at least one.这可以通过析取(访问当前城市的路径的布尔变量或布尔变量)或通过要求相关路径的总和至少为一个来完成。

In other words, you should only need a single variable per path.换句话说,每个路径只需要一个变量。 There is no need to find out which path visits a city.无需找出访问城市的路径。

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

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