公平产品分配算法

发布于 2024-09-30 23:40:22 字数 586 浏览 8 评论 0原文

这是我的问题:

  • 有n家公司在分发 产品。
  • 所有产品应在 k 天内分发出去。
  • Ci 公司分发的产品应该是连续的 - 这意味着可以在第 2、3、4、5 天分发,但不能是
  • Ci 公司分发的产品数量为 2、3、6、7。第 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,第二天有 2 个产品 A,所以产品 A 的分布不是非递减

编辑 如果存在无法公平分配的情况,请提供简短的描述,我会接受答案

Here is my problem:

  • There are n companies distributing
    products.
  • All products should be distributed in k days
  • Distributing products of company Ci should be consecutive - it means that it can be distributed on days 2,3,4,5 but not 2,3,6,7
  • number of distributed products by company Ci on day j should be less than (or equal) on day j-1 (if there were any on day j-1)
  • difference between distributed products between days i and j should not be greater than 1

Example:

We have 3 days to distribute products. Products of company A: a,a,a,a,a. Products of company B: b,b,b. Products of company C: c,c

Fair distribution:
[aab,aabc,abc]

Invalid distribution:
[aabc,aabc,ab]
because on 1st day there are 4 products, on 3rd day 2 products (difference > 1)

Invalid distribution:
[abc,aabc,aab]
because on 1st day there is one product A, and on 2nd day there are 2 products A, so distribution of product A is not non-decreasing

EDIT
if there is a case that makes fair distribution impossible please provide it with short description, I'll accept the answer

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

西瑶 2024-10-07 23:40:22

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() ,它们只是执行您所期望的操作。)

Gareth Rees's comment on djna's answer is right -- the following counterexample is unsolvable:

  • 3 days, 7 items from company A and 5 items from company B

I tested this with the following dumbest-possible brute-force Perl program (which takes well under a second, despite being very inefficient):

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";
                        }
                    }
                }
            }
        }
    }
}

Please have a look and verify that I haven't made any stupid mistakes. (I've omitted max() and min() for brevity -- they just do what you'd expect.)

你列表最软的妹 2024-10-07 23:40:22

因为我认为这个问题很有趣,所以我做了一个使用 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 都非常简单,并且具有大量分解。为了解决更大的实例,人们可能应该用更智能的变体替换它们(例如通过使用常规约束)。

Since I thought the problem was fun, I did a model for finding solutions using MiniZinc. With the Gecode backend, the initial example is shown to have 20 solutions in about 1.6 ms.

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;

The predicates decreasing_except_0 and consecutive are both very naive, and have large decompositions. To solve larger instances, one should probably replace them with smarter variants (for example by using the regular constraint).

烟火散人牵绊 2024-10-07 23:40:22

已经证明,第 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 使得:

  • C(i,A) > 。 0且C(j,A)> 0
  • 对于任何一天 x 使得 x < i或x> j, 对于任意一天 x,C(x,A) = 0
  • ,使得 i < x < j, C(x,A) = C(x)

诚然,问题就变得微不足道了:)

It has been shown that the points 4 and 5 were incompatible:

  • 4: For any day j, for any company A, C(j,A) == 0 or C(j,A) >= C(j+1,A)
  • 5: For any days i and j, |C(i) - C(j)| <= 1

You thus need relaxing either constraint. Honestly, while I get a feeling of why 4 was put in place (to avoid delaying the distribution of one company indefinitely) I think it could be expressed otherwise to consider the first and last day of distribution as being special (since on the first day, you typically take what's left by the previous company and on last day you distribute what's left).

Point 3 does force the contiguity.

Mathematically:

For any company A, which has products, there exists two days i and j such that:

  • C(i,A) > 0 and C(j,A) > 0
  • for any day x such that x < i or x > j, C(x,A) = 0
  • for any day x such that i < x < j, C(x,A) = C(x)

Admittedly, the problem then becomes trivial to solve :)

北城孤痞 2024-10-07 23:40:22

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

考虑 4 天,供应商 A 提供 6 件商品,供应商 B 提供 6 件商品。

I don't think that you can always fulfil your requirements.

Consider 4 days, and 6 items from supplier A and 6 items from supplier B.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文