注意:这是关于在SWF文件中排序记录的现实问题的抽象重写。解决方案将帮助我改进开源应用程序。
鲍勃有一家商店,想要出售。他的商店里有很多产品,每种产品都有一定数量的单位库存。他还有许多货架价格标签(与产品数量一样多),价格已经印在他们身上。他可以在任何产品上放置任何价格标签(该产品的整个库存的一个项目的单一价格),但是有些产品有额外的限制 - 任何此类产品可能不比某个其他产品便宜。您必须找到如何安排价格标签,以便所有Bob商品的总成本尽可能低。总成本是每个产品的分配价格标签的总和乘以库存中的产品数量。
假设:
该计划必须找到:
满足条件:
请注意,如果不是第一个条件,解决方案就是按价格和产品按数量对标签进行排序,并直接匹配。
输入的典型值为N,K <10000。在现实生活中,只有几个不同的价格标签(1,2,3,4)。
以下是大多数简单解决方案(包括拓扑排序)不起作用的一个例子:
您有10个数量为1到10的商品,以及10个价格标签,价格为1到10美元。有一个条件:数量为10的物品不得低于数量为1的物品。
最佳解决方案是:
Price, $ 1 2 3 4 5 6 7 8 9 10
Qty 9 8 7 6 1 10 5 4 3 2
总费用为249美元。如果将1,10对放在任何一个极端附近,总成本会更高。
答案 0 :(得分:16)
对于一般情况,问题是NP完全的。这可以通过减少3分区(这是一个仍然很强的NP-complete版本的bin包装)来显示。
让 w 1 ,...,w n 为3分区实例的对象权重,让 b 是bin大小, k = n / 3 允许填充的bin数。因此,如果可以对对象进行分区,则每个bin只有3个对象,就有一个3分区。
对于减少,我们设置N = kb 并且每个bin由相同价格的 b 价格标签表示(想想P i 增加每个 b 标签)。设 t i ,1≤ i ≤ k ,是对应的标签的价格我是
对于 t i =(2b + 1) i ,1≤ i ≤ k ,当且仅当Bob可以出售 2b Σ1≤ i ≤ k 时才有3分区 t i :
所以,这是破坏性的部分;-)但是,如果不同价格标签的数量是常数,您可以使用动态编程在多项式时间内解决它。
答案 1 :(得分:9)
这个问题类似于CS文献中考虑的许多调度问题。请允许我重申它。
问题(“具有优先级,权重和一般迟到惩罚的非抢先式单机调度”)
输入:
工作1,...,n
工作中的“树状”优先关系预测(Hasse图是森林)
权重w 1 ,...,w n
从{1,...,n}到 Z +
输出:
通讯:工作&lt; =&gt;产品;我预先j&lt; =&gt;我的价格低于j;重量&lt; =&gt;数量; L(t)&lt; =&gt; t th 最低价格
当L是线性时,由于Horn [1],存在一种有效的多项式时间算法。 这篇文章是支付墙的背后,但主要的想法是
对于所有j,找到仅包含j及其后继者的平均权重最大的连接作业集。 例如,如果n = 6并且优先约束是1 prec 2和2 prec 3和2 prec 4和4 prec 5,则考虑2的集合是 {2},{2,3},{2,4},{2,3,4},{2,4,5},{2,3,4,5}。 实际上我们只需要最大平均权重,可以通过动态编程自下而上计算。
按照相关集合的平均权重顺序安排工作。
在CyberShadow的例子中,我们有n = 10和1 prec 10和w j = j和L(t)= t。 步骤1中计算的值是
工作1:5.5(平均值为1和10)
工作2:2
工作3:3
工作4:4
工作5:5
工作6:6
工作7:7
工作8:8
工作9:9
工作10:10
最佳顺序为9,8,7,6,1,10,5,4,3,2。
即使对于不同的L选择,该算法在实践中也可以很好地工作,因为最优性证明使用局部改进。 或者,也许CS Theory Stack Exchange上的某个人会有一个想法。
[1] W. A. Horn。 具有树状优先顺序排序和线性延迟惩罚的单机作业排序。 SIAM应用数学杂志,Vol。 23,No。2(1972年9月),第189-202页。
答案 2 :(得分:4)
由于我认为问题很有趣,我做了一个使用约束编程寻找解决方案的模型。该模型使用名为MiniZinc的建模语言编写。
include "globals.mzn";
%%% Data declaration
% Number of products
int: n;
% Quantity of stock
array[1..n] of int: stock;
% Number of distinct price labels
int: m;
% Labels
array[1..m] of int: labels;
constraint assert(forall(i,j in 1..m where i < j) (labels[i] < labels[j]),
"All labels must be distinct and ordered");
% Quantity of each label
array[1..m] of int: num_labels;
% Number of precedence constraints
int: k;
% Precedence constraints
array[1..k, 1..2] of 1..n: precedences;
%%% Variables
% Price given to product i
array[1..n] of var min(labels)..max(labels): prices :: is_output;
% Objective to minimize
var int: objective :: is_output;
%%% Constraints
% Each label is used once
constraint global_cardinality_low_up_closed(prices, labels, num_labels, num_labels);
% Prices respect precedences
constraint forall(i in 1..k) (
prices[precedences[i, 1]] <= prices[precedences[i, 2]]
);
% Calculate the objective
constraint objective = sum(i in 1..n) (prices[i]*stock[i]);
%%% Find the minimal solution
solve minimize objective;
问题的数据在单独的文件中给出。
%%% Data definitions
n = 10;
stock = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
m = 10;
labels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
num_labels = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
k = 1;
precedences = [| 1, 10 |];
该模型相当天真,直截了当,没有花哨的东西。使用Gecode后端来解决示例问题,生成以下输出(假设模型在model.mzn中,数据在data.dzn中)
$ mzn2fzn -I/usr/local/share/gecode/mznlib/ model.mzn data.dzn
$ fz -mode stat -threads 0 model.fzn
objective = 265;
prices = array1d(1..10, [1, 10, 9, 8, 7, 6, 5, 4, 3, 2]);
----------
objective = 258;
prices = array1d(1..10, [2, 10, 9, 8, 7, 6, 5, 4, 1, 3]);
----------
objective = 253;
prices = array1d(1..10, [3, 10, 9, 8, 7, 6, 5, 2, 1, 4]);
----------
objective = 250;
prices = array1d(1..10, [4, 10, 9, 8, 7, 6, 3, 2, 1, 5]);
----------
objective = 249;
prices = array1d(1..10, [5, 10, 9, 8, 7, 4, 3, 2, 1, 6]);
----------
==========
%% runtime: 0.027 (27.471000 ms)
%% solvetime: 0.027 (27.166000 ms)
%% solutions: 5
%% variables: 11
%% propagators: 3
%% propagations: 136068
%% nodes: 47341
%% failures: 23666
%% peak depth: 33
%% peak memory: 237 KB
对于较大的问题,它当然要慢得多,但随着时间的推移,该模型通常会产生更好的解决方案。
答案 3 :(得分:3)
作为社区维基发布一些想法,随时可以编辑。
如果您考虑额外的约束条件,必须按照每个节点必须位于其父节点右侧的方式布局或重新排列一组自上而下的树,这个问题更容易可视化(左边的产品更便宜,而右边的产品更贵)。
假设两个产品冲突,如果第一个产品的库存多于第二个产品,那么第一个产品的价格必须不比另一个产品便宜(因此它们被“拉”到不同方向的价格-明智的)。同样,冲突的产品组是至少有两种产品存在冲突的产品,其产品不会与产品组外的任何产品发生冲突。
我们可以做一些观察:
该算法的主要问题是如何处理已经放置的约束对的位移。我想,只是试图通过迭代搜索重新放置移位链可能会起作用,但算法看起来已经过于复杂而无法正常工作。
如果不同价格的数量较少,您可以为每个不同的价格使用双端队列(或双向链接列表),保留分配给它们的所有项目。这些deques的价格从最低到最高。将项目插入双端队列会将最后一个项目移动到下一个双端队列的开头(对于下一个更高的不同价格),等等之后的所有deques。
关于迭代/冒泡排序算法需要注意的一点是:当你有一对冲突的产品时,贪婪地向任一方向走一个位置是不够的,直到下一个方向没有产生改进。以下是我用 playing around a bit得到的一个测试用例,Mathematica 写了test case generator:
Price, $ 1 2 7 9
Qty 3 2 1 4
约束是将1-qty项目右侧的4-qty项目。如上所示,总价为50美元。如果你将一对位置向左移动(所以它是3 1 4 2
),则总计最高可达51美元,但如果再往前走一次(1 4 3 2
)则会降至48美元。
答案 4 :(得分:3)
这是Gero's answer的后续行动。我的想法是修改他的结构,以显示出强大的NP硬度。
选择$ t_i = i $,而不是选择$ t_i =(2b + 1)^ i $。现在,您必须修改具有奖金$ P = 2b \ sum_ {1 \ leq i \ leq k} t_i $的解决方案的参数意味着存在3分区。
采取任意货架订单。通过以下方式进行会计核算:将$ w_i-1 $单位数量的根产品分配给其叶子产品。然后每个产品都有数量2.根据约束的定义,这不会转移到更高的价格。 在此转变之后,价格将正好是P $。 如果转移移动了一些数量到较低的奖金,原始奖金严格大于$ P $。
因此,如果所有叶子产品与其根产品具有相同的奖励,则只能实现所声称的奖品,这意味着存在3分区。
引用SWAT 2010 paper这个参数的结果表明,即使使用一元编码的数字和$ k $不同的价格标签,运行时间为$ f(k)\ cdot n ^ {O(1) } $会违反“标准复杂性假设”。 这使得暗示动态编程的运行时间为$ n ^ {O(k)} $看起来并不那么糟糕。
这是在cstheory的同一answer交叉发布的。
答案 5 :(得分:1)
您可以尝试首先解决更简单的情况,您只需要按价格和产品按数量对标签进行排序,并直接匹配,然后在第一个近似值上使用进化过程:生成有序产品列表的随机变体你有,在列表中向上或向下移动少量随机选择的项目,计算列表中每个变体的总成本,保留最好的几个,并使其成为下一代的基础。我希望,最终迭代这个过程最终会给你正确的答案,只需要一小部分时间来强制解决问题。
答案 6 :(得分:1)
解决问题的一种方法是使用0-1 linear programming表达它并使用Balas的加法算法解决它。以下是问题的编码方式:
我不是线性编程专家,可能存在更高效的编码。
答案 7 :(得分:0)
以字典顺序生成价格的排列,并返回符合约束条件的第一个。
假设产品和价格已经分类(分别为最少,最高,最高),
l[k] = k+1
和0 <= k < n
设置l[n] = 0
。然后设置k = 1
。p = 0, q = l[0]
。M[k] = q
。如果特定涉及P[k]
的任何价格约束失败,请转到5.否则,如果k = n
,请返回M[1]...M[n]
。u[k] = p, l[p] = l[q], k = k + 1
并转到2. p = q, q = l[p]
。如果q != 0
转到3。k = k - 1
,并在k = 0
时终止。否则,请设置p = u[k], q = M[k], l[p] = q
并转到5. 这是Knuth的计算机编程艺术,第4卷,分册2,第7.2.1.2节中的算法X(稍作修改)。与Knuth的大多数算法一样,它使用基于1的索引。将其隐藏为适合您对典型编程语言的基于0的索引,我将其作为练习留给读者。
修改强>
不幸的是,事实证明这并不能保证非递减序列。我不得不多考虑一下,看看是否可以挽救它。