【问题标题】:Constraint to select certain items in MiniZinc限制在 MiniZinc 中选择某些项目
【发布时间】:2020-12-02 14:07:03
【问题描述】:

我正在尝试创建一个模型来处理源自 Set Packing 问题的问题。

在本例中,我有一个名为 nRecipes 的参数表示食谱的数量,另一个名为 nIngred 的参数表示成分的数量。每个食谱都有特定的营养价值,由 value 参数表示。

我想选择最多有numDishes个菜谱的菜单,让菜单的营养价值最大化。另一个限制是,每道所选菜肴均不含其他所选菜肴中的任何成分。

我尝试了以下方法:

int: nRecipes;                                  
set of int: RECIPES = 1..nRecipes;              

array[RECIPES] of int: value;                   

int: nIngred;                             
set of int: INGREDIENTS = 1..nIngred;    
int: numDishes;                                 
  
array[INGREDIENTS] of set of RECIPES: group;

var set of RECIPES: menu; 


array[1..numDishes] of var RECIPES: selected_menu;

constraint menu <= selected_menu; % Error
constraint alldifferent(selected_menu);

solve maximize value;  

测试示例:

nRecipes = 10;
value = [10,8,4,2,6,9,5,3,8,10];
group = [{1,4,6},{1,2,6,7},{1,3,6,8}, 
        {1,2,3},{2,9,10},{5,6,8,10},
        {7,8,10},{1,3,5}];
numDishes = 3;
nIngred = 8;

预期输出:

Value:20
Recipe:{1,10}

【问题讨论】:

    标签: constraint-programming minizinc


    【解决方案1】:

    这是一种方法,其中 group 变量已被翻转以保存每个配方的成分(也将其重命名为 ingredients)。现在您可以使用all_disjoint 来制定所选菜肴不得使用相同食材的约束条件。

    include "globals.mzn";
    
    int: nRecipes;                                  
    set of int: RECIPES = 1..nRecipes;              
    
    array[RECIPES] of int: value;                   
    
    int: nIngred;                             
    set of int: INGREDIENTS = 1..nIngred;    
    int: numDishes;                                 
        
    array[RECIPES] of set of INGREDIENTS: ingredients;
    
    array[1..numDishes] of var RECIPES: selected_menu;
    
    constraint all_disjoint([ingredients[selected_menu[i]] | i in 1..numDishes]);
    
    var int: obj = sum([value[selected_menu[i]] | i in 1..numDishes]);
    solve maximize obj;
    
    output ["total value=\(obj)\n"] ++ 
    ["selected_menu=\(selected_menu)\n"] ++
    ["ingredients=\([ingredients[selected_menu[i]] | i in 1..numDishes])\n"];
    

    使用数据运行:

    numDishes = 2;
    nIngred = 8;
    nRecipes = 10;
    value = [10,8,4,2,6,9,5,3,8,10];
    
    ingredients = [{1,2,3,4},{2,4,5},{3,4,8}, 
            {1},{6,8},{1,2,3,6},
            {2,7},{3,6,7},{5},{5,6,7}];
    

    给予:

    total value=20
    selected_menu=[10, 1]
    ingredients=[5..7, 1..4]
    

    编辑: 新版本允许选择比num_dishes 更少的菜品,如果这样可以提供更好的客观价值。这是通过变量dishes 完成的,该变量表示实际选择的菜肴数量。未使用的菜单将具有 0 的值。

    int: nRecipes;                                  
    set of int: RECIPES = 1..nRecipes;              
    set of int: RECIPES0 = 0..nRecipes;
    
    array[RECIPES] of int: value;                   
    
    int: nIngred;                             
    set of int: INGREDIENTS = 1..nIngred;    
    int: numDishes;                                 
        
    var 1..numDishes: maxDishes;
        
    array[INGREDIENTS] of set of RECIPES: group;
    
    array[1..numDishes] of var RECIPES0: selected_menu;
    
    constraint forall(i in INGREDIENTS)
        (sum(m in selected_menu)(m in group[i]) <= 1);
    
    var int: obj = sum([value[selected_menu[i]] | i in 1..maxDishes]);
    solve maximize obj;
    
    output["Value: ", show(obj), "\nRecipe: ", show([selected_menu[d] | d in 1..maxDishes])];
    

    编辑:另一个版本使用一个集合来表示selected_menu

    int: nRecipes;                                  
    set of int: RECIPES = 1..nRecipes;              
    
    array[RECIPES] of int: value;                   
    
    int: nIngred;                             
    set of int: INGREDIENTS = 1..nIngred;    
    int: numDishes;
                                
    array[INGREDIENTS] of set of RECIPES: group;
    
    var 1..numDishes: dishes;
    var set of RECIPES: selected_menu;
    
    % each ingredients may appear in at most one recipe
    constraint forall(i in INGREDIENTS)
        (card(selected_menu intersect group[i]) <= 1);
        
    constraint card(selected_menu) <= dishes;    
    
    var int: obj = sum([value[m] | m in selected_menu]);
    solve maximize obj;
    
    output["Value: ", show(obj), "\nRecipe: ", show(selected_menu)];
    

    【讨论】:

    • 添加了使用原始参数的版本。
    • card() 函数给出了集合的基数。 array2set() 函数将数组selected_menu 转换为一个集合(以便我们可以使用intersect)。
    • 您可以在输出语句中使用sort(selected_menu) 或添加对称破坏约束,如constraint increasing(selected_menu);
    • 如果你转换成 set (array2set()) 你会得到想要的输出。
    • output["Recipe: ", show(array2set(selected_menu))]; 将提供所需的输出。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多