簡體   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