公平产品分配算法

6

下面是我的问题:

  • 有n家公司分销产品。
  • 所有产品应该在k天内分配完毕。
  • 分配公司Ci的产品应该连续进行,这意味着它可以在第2、3、4、5天分配,但不能在第2、3、6、7天分配。
  • 公司Ci在第j天分配的产品数量应该比第j-1天少(或相等)(如果第j-1天有分配的话)。
  • 第i天和第j天分配的产品之间的差异不应大于1。

例子:

我们有3天时间来分配产品。公司A的产品:a,a,a,a,a。公司B的产品:b,b,b。公司C的产品:c,c

公平分配: [aab,aabc,abc]

无效分配: [aabc,aabc,ab] 因为第一天有4个产品,第三天有2个产品(差异>1)

无效分配: [abc,aabc,aab] 因为第一天只有一个A产品,第二天有两个A产品,所以A产品的分配不是非递减的。

编辑 如果有一种情况使公平分配变得不可能,请提供简短的描述,我会接受答案。


1
你好像漏掉了一个特殊情况: 公司Ci在第j天的分布产品数量应该比第j-1天少,但是在你的公平例子中,第一天没有任何“c”,而第二天有一个“c”。 - djna
2
您的第四个项目点是指小于或等于,而不是小于吗? - Jackson
1
点5:应该公平分配,天数之间的差异不应超过1。 - dfens
你是在什么情境下尝试解决这个问题?如果问题总是以相同的方式定义并且非常关键,可能需要使用专门的算法。如果问题可能会改变或者你只需要一个简单的解决方案,那么使用像约束编程这样的东西可能更好。此外,问题实例的预期大小也很有趣,例如公司数量、产品数量等等。 - Zayenz
1
@belisarius:现在我明白了,每天分配的产品总数在所有天数内最多只能相差1个。尽管这是OP的第5点,但表述比较抽象——可以想象句子开头是“对于任意的i和j天”。 - j_random_hacker
显示剩余2条评论
4个回答

3

Gareth Rees对djna的回答进行的评论是正确的 - 以下反例是无解的:

  • 3天时间,从公司A取7个项目,从公司B取5个项目

我使用了下面这个最简单的Perl程序进行测试(尽管效率很低,但仍然在不到一秒钟的时间内完成):

my ($na, $nb) = (7, 5);
for (my $a1 = 0; $a1 <= $na; ++$a1) {
    for (my $a2 = 0; $a2 <= $na - $a1; ++$a2) {
        my $a3 = $na - $a1 - $a2;
        for (my $b1 = 0; $b1 <= $nb; ++$b1) {
            for (my $b2 = 0; $b2 <= $nb - $b1; ++$b2) {
                my $b3 = $nb - $b1 - $b2;
                if ($a1 >= $a2 && $a2 >= $a3 || $a1 == 0 && $a2 >= $a3 || $a1 == 0 && $a2 == 0) {
                    if ($b1 >= $b2 && $b2 >= $b3 || $b1 == 0 && $b2 >= $b3 || $b1 == 0 && $b2 == 0) {
                        if (max($a1 + $b1, $a2 + $b2, $a3 + $b3) - min($a1 + $b1, $a2 + $b2, $a3 + $b3) <= 1) {
                            print "Success! ($a1,$a2,$a3), ($b1,$b2,$b3)\n";
                        }
                    }
                }
            }
        }
    }
}

请看一下并验证我没有犯任何愚蠢的错误。(为了简洁起见,我省略了max()min() - 它们只是做你所期望的事情。)

{a,a,a,a,a} {a,b,b,b} {a,b,b} 是无效的吗? - Dr. belisarius
1
@belisarius:是的,它违反了第五个条件(5-3>1)。 - Matthieu M.
@Matthiew,请查看之前的评论。 - Dr. belisarius
@belisarius:什么是“条件6”?有5个条件,第5个条件表示每天分配的产品总数在所有天数内最多只能相差1个。 - j_random_hacker
@dfens和我想的一样。我认为问题不够清晰。请看最近@Matthieu的回答。 - Dr. belisarius
显示剩余4条评论

2

因为我认为这个问题很有趣,所以我使用MiniZinc创建了一个寻找解决方案的模型。使用Gecode后端,在大约1.6毫秒内得到了20个解决方案的初始示例。

include "globals.mzn";

%%% Data
% Number of companies
int: n = 3;
% Number of products per company
array[1..n] of int: np = [5, 3, 2];
% Number of days
int: k = 3;

%%% Computed values
% Total number of products
int: totalnp = sum(np);
% Offsets into products array to get single companys products 
% (shifted cumulative sum).
array[1..n] of int: offset = [sum([np[j] | j in 1..i-1]) 
                          | i in 1..n];

%%% Predicates
predicate fair(array[int] of var int: x) =
      let { var int: low,
            var int: high
      } in
        minimum(low, x) /\
        maximum(high, x) /\
        high-low <= 1;
predicate decreasing_except_0(array[int] of var int: x) =
        forall(i in 1..length(x)-1) (
                 (x[i] == 0) \/
                 (x[i] >= x[i+1])
        );
predicate consecutive(array[int] of var int: x) =
        forall(i in 1..length(x)-1) (
             (x[i] == x[i+1]) \/
             (x[i] == x[i+1]-1)
        );

%%% Variables
% Day of production for all products from all companies
array[1..totalnp] of var 1..k: products 
          :: is_output;
% total number of products per day
array[1..k] of var 1..totalnp: productsperday 
        :: is_output;

%%% Constraints 
constraint global_cardinality(products, productsperday);
constraint fair(productsperday);
constraint
    forall(i in 1..n) (
         let { 
             % Products produced by company i
             array[1..np[i]] of var int: pi
                = [products[j] | 
                 j in 1+offset[i]..1+offset[i]+np[i]-1],
             % Products per day by company i
             array[1..k] of var 0..np[i]: ppdi
         } in
           consecutive(pi) /\
           global_cardinality(pi, ppdi) /\
           decreasing_except_0(ppdi)
    );

%%% Find a solution, default search strategy
solve satisfy;

谓词decreasing_except_0consecutive都非常朴素,并且具有大量的分解。为了解决更大的实例,可能应该用更智能的变体来替换它们(例如,使用正则约束)。


1

已经证明,点4和点5不兼容:

  • 4:对于任何一天j,对于任何公司A,C(j,A) == 0或C(j,A) >= C(j+1,A)
  • 5:对于任何两天i和j,|C(i) - C(j)| <= 1

因此,您需要放宽其中一个约束条件。老实说,虽然我能感受到为什么要设置4(为了避免无限期延迟一个公司的分配),但我认为可以用其他方式表达,以考虑分配的第一天和最后一天是特殊的(因为在第一天,您通常会拿走前一个公司剩下的东西,在最后一天,您会分配剩下的东西)。

点3确实强制连续性。

数学上:

对于任何有产品的公司A,存在两天i和j,使得:

  • 对于任何的i和j,C(i,A) > 0且C(j,A) > 0
  • 对于任何的x,如果x < i或者x > j,则C(x,A) = 0
  • 对于任何的x,如果i < x < j,则C(x,A) = C(x)

可以承认,这个问题变得非常容易解决了 :)


0

我认为你不能总是满足你的要求。

考虑4天,从供应商A购买6件物品和从供应商B购买6件物品。


1
[a,a,a][a,a,a][b,b,b][b,b,b] 或者我漏掉了什么? - mcveat
2
3天,从A中选7个项目,从B中选5个项目怎么样? - Gareth Rees
[b, b, b, b, b][a, a, a, a][a, a, a]或者我又错过了什么?我一直在寻找无法解决的情况,但还是没有运气... - mcveat
@mcveat:失败的原因是第一天有5个产品,而第三天只有3个产品,而且任意两天之间的差异最多为1。 - j_random_hacker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接