【问题标题】:Minizinc piecewise linear functionMinizinc 分段线性函数
【发布时间】:2020-09-27 00:26:27
【问题描述】:

我有一个成本最小化问题。在这个问题中,我们有 3 个供应商。他们是 S1、S2 和 S3。 其中成本计算为价格 x 购买数量。

3 家供应商的价格值如下。

price=[10,15,20]

如果我有一个决策变量array[1..3] of var int:purchase,我将从哪个供应商那里获得优化问题的答案,我需要购买多少单位。

示例代码如下。

enum suppliers={s1,s2,s3};
int: len_sup=length(suppliers);
set of int:range_sup=1..len_sup;

array[range_sup] of int:price=[10,15,20];
int:demand=40;

array[range_sup] of int:moq=[5,0,5];
array[range_sup] of int:maxoq=[8,20,30];

array[range_sup] of var 0..100:purchase;

constraint sum(i in range_sup)(
purchase[i])=demand
;

constraint forall(i in range_sup)(
purchase[i]>=moq[i] \/ purchase[i]=0
);

constraint forall(i in range_sup)(
purchase[i] <= maxoq[i]
);

var int:cost;

constraint sum(i in range_sup)(
purchase[i]*price[i])=cost;

solve minimize cost;

现在我有一个嵌入价格折扣的新要求。这个价格折扣肯定会与我们购买的数量有所不同。

对于这个例子,假设我们知道,如果我们从任何供应商那里购买数量为 1 到 5,那么我们将获得 5% 的折扣(价格将从 5% 下降)。如果我们购买 6 到 10,我们将获得 10 % 价格折扣。如果我们购买超过 11 个,我们将获得 15% 价格折扣。

你如何将这个东西合并到模型中。这是通过分段线性函数使用的吗?(因为它看起来像非线性问题)。有人可以说明如何使用分段线性函数。

【问题讨论】:

    标签: minizinc


    【解决方案1】:

    也许这并不能真正回答您的问题,但对于某些求解器(例如 Gecode、JaCoP 和 OptiMathSAT),您可以为此使用查找表。

    您可以添加一个简单的折扣表:

    array[1..3,1..3] of float: discount = array2d(1..3,1..3,
    [
     1,5,0.05,  % between 1 and 5: 5% discount
     6,10,0.10, % between 6 and 10: 10% discount
     11,1000,0.15, % 11.. 5: 15% discount
    ]);
    

    并将成本约束更改为

    constraint
    cost >= 0 /\
    cost =
    sum(i in range_sup) (
       purchase[i]*price[i]
        % lookup the discount 
        - purchase[i]*price[i]*sum(r in 1..3) (
              discount[r,3]*(purchase[i] >=  discount[r,1] /\
              purchase[i] <= discount[r,2]
          )
     ;
    

    最佳解决方案将是

    purchase = array1d(1..3, [8, 20, 12]);
    cost = 531;
    

    如前所述,这仅适用于支持 var float 变量以及非线性 float_times 的求解器。

    更新:suresh_chinthy 询问了piecewise_linear。这是一个如何使用它的示例。请注意,它适用于 Gecode、JaCoP、OptiMathSAT,但不适用于 CBC 等线性求解器,因为piecewise_linear 返回一个var float,它与决策变量purchase[i](即var inte)相乘,而这不是在 CBC 中允许。

    使用这两个数组更改上面代码中的discount 表。这里我们假设可以购买 0 到 40 件商品。

    % the amount of purchase
    array[0..40] of float: discount_base = array1d(0..40,[i | i in 0..40]);
    % the discounts for each purchase
    array[0..40] of float: discount =
       array1d(0..40,
               [0.0, % 0
                0.05, 0.05, 0.05, 0.05, 0.05, % 1-5
                0.10,0.10,0.10,0.10,0.10, % 6-10,
                0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15, % 11-20
                0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15, % 21-30
                0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15,0.15, % 31-40                        
                ]);
    

    并且cost 约束更改为

    constraints
      cost > 0 /\
      cost = 
           sum(i in range_sup) (
           purchase[i]*price[i] - purchase[i]*price[i]* piecewise_linear(1.0*purchase[i], discount_base, discount)
      );
    

    结果同上:

    purchase = array1d(1..3, [8, 20, 12]);
    cost = 531;
    

    【讨论】:

    • 感谢@hakank 的建议,只需要澄清 ....function var float: piecewise_linear(var float: x, array [int] of float: xi, array [int] of float: vi) 可以应用于此。 (用户指南中提到的分段线性函数)
    • 因为目前我的模型只能从 coin-bc 求解。 (不是上面的示例代码)
    • 这取决于您将如何处理piecewise_linear 返回的值。由于它返回var float,CBC 将不允许您将另一个var int/float 与它相乘(因为CBC 不支持float_times)。但是您可以将它与加法和减法一起使用。我还将用一个如何使用分段线性的示例来更新我的答案(仍然仅适用于 Gecode 等)。
    • 感谢您的明确解释。我认为我对每个求解器的限制和每个求解器的相同特征缺乏了解。你能推荐一个我可以学习求解器比较的地方吗(哪个求解器在哪个场景等..)。非常感谢您的宝贵反馈。
    • MiniZinc 网站在minizinc.org/doc-2.4.3/en/solvers.html 上提到了其中的一些内容,但我不知道有更系统的比较。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-17
    • 2018-05-01
    • 2022-10-09
    相关资源
    最近更新 更多