我有一个成本优化请求,我不知道如何有文献。这有点难以解释,所以我提前为问题的长度道歉。
我正在访问的服务器以这种方式工作:
T((r1, ..., rn)x(f1, ..., fp) = a + b * n * p
不失一般性(仅通过规范化),我们可以假设b=1
所以成本是:
T((r1, ...,rn)x(f1,...fp)) = a + n * p
(r1, f(r1)), ... (rk, f(rk))
的子集,这是一个来自用户的请求。我的程序充当用户和服务器(外部)之间的中间人。我有很多这样的请求(每天数万个)。从图形上看,我们可以将其视为一个n x p稀疏矩阵,我想用矩形子矩阵覆盖非零值:
r1 r2 r3 ... rp ------ ___ f1 |x x| |x| f2 |x | --- ------ f3 .. ______ fn |x x| ------
有:
我将g命名为我的问题的稀疏系数(所需对的数量超过总可能对,g = k / (n * p)
。我知道系数a
。
有一些明显的观察结果:
k * (a + 1) = g * n * p * (a + 1)
a + n * p
g > g_min = 1/ (a+1) * (1 + 1 / (n * p))
f1 f2 f3 r1 x x r2 x r3 x x
可以重新排序为
f1 f3 f2 r1 x x r3 x x r2 x
有一个最佳解决方案是请求(f1,f3) x (r1,r3) + (f2) x (r2)
for each permutation on rows: (n!) for each permutation on columns: (p!) for each possible covering of the n x p matrix: (time unknown, but large...) compute cost of the covering
所以我正在寻找一个近似的解决方案。我已经有了某种贪婪算法,它找到了一个给定矩阵的覆盖(它以单元格开始,如果合并中空单元格的比例低于某个阈值,则合并它们。)
为了记住一些数字,我的n介于1到1000之间,而我的介于1到200之间。覆盖模式实际上是“块状的”,因为记录来自于所要求的字段类似的类。不幸的是我无法访问记录类......
问题1 :有人有想法,聪明的简化,还是对可能有用的论文的参考?由于我有很多请求,一个平均平均的算法是我正在寻找的(但我无法承受它在某些极端情况下工作得很差,例如请求整个当n和p很大时,矩阵,请求确实很稀疏。)
问题2 :事实上,问题更复杂:实际上成本更像是形式:a + n * (p^b) + c * n' * p'
,其中b是常数< 1(一旦记录被要求提供字段,要求其他字段的成本并不太高)并且n' * p' = n * p * (1 - g)
是我不想要求的单元格数量(因为它们无效,并且有一个请求无效事物的额外费用)。我甚至不能梦想快速解决这个问题,但仍然......任何人的想法?
答案 0 :(得分:5)
选择子矩阵以覆盖请求的值是set covering problem的一种形式,因此NP完成。您的问题增加了这个已经很难解决的问题,即集合的成本不同。
您允许排列行和列不是一个大问题,因为您可以只考虑断开连接的子矩阵。第一行,第四列到第七行,第五列,第四列,第二列是有效集,因为您可以只交换第二行和第五行并获得连接的子矩阵行一,第四列到第二行,第七列。当然这会增加一些限制 - 并非所有集合在所有排列下都是有效的 - 但我不认为这是最大的问题。
维基百科的文章给出了不可近似性的结果,即在多项式时间内不能用因子0.5 * log2(n)
来解决问题,其中n
是集合的数量。在你的情况下,2^(n * p)
是一个(相当悲观)的上限集合和数量,你只能在多项式时间内找到一个因子0.5 * n * p
的解决方案(除了N = NP并忽略)不同的成本)。
忽略行和列排列的集合数的乐观下限为0.5 * n^2 * p^2
,产生更好的log2(n) + log2(p) - 0.5
因子。因此,您只能期望在最坏情况下找到一个解决方案n = 1000
和p = 200
,在乐观情况下最多约为17
,最多可达{{{1}。 1}}在悲观情况下(仍然忽略不同的成本)。
因此,您可以做的最好的事情是使用启发式算法(维基百科文章提到了几乎最优的贪婪算法),并接受算法执行(非常)坏的情况。或者你走另一条路并使用优化算法,并尝试找到一个好的解决方案,使用更多的时间。在这种情况下,我建议尝试使用A* search。
答案 1 :(得分:1)
我确信在某个地方有一个非常好的算法,但这是我自己的直观想法:
抛出一些矩形的方法:
生长
收缩
四
另外:请记住有时最好有两个重叠矩形,而不是一个大矩形,这是它们的超集。例如。两个矩形在一个角落重叠的情况。
答案 2 :(得分:1)
好的,我对这个问题的理解已经改变了。新想法:
将每一行存储为长位字符串。和位串对,试图找到最大化1位数的对。将这些对成长为更大的组(排序并尝试将真正的大对彼此匹配)。然后构造一个将命中最大组的请求,然后忘记所有这些位。重复完成所有操作。也许有时会从行切换到列。
查找其中包含零点或几点的所有行/列。暂时“删除”它们。现在,您正在查看将其排除的请求所涵盖的内容。现在也许应用其他技术之一,然后处理被忽略的行/列。另一种思考方式是:先处理更密集的点,然后再使用更稀疏的点。
答案 3 :(得分:0)
我认为用户请求中提到的n个记录(行)和p字段(cols)设置为p维空间({0,1} ^ p)中的n个点,第i个坐标为1 iff it有一个X和identify a hierarchy of clusters,根目录中包含所有X的最粗集群。对于集群层次结构中的每个节点,请考虑覆盖所有所需列的产品(这是行(任何子节点)x cols (任何子节点))。然后,自下而上决定是否合并子覆盖物(支付整个覆盖物),或将它们作为单独的请求保留。 (覆盖物不是连续的列,而是完全需要的那些;即考虑一点矢量)
我同意Artelius的意见,即重复的产品要求可能更便宜;我的等级方法需要改进才能纳入。
答案 4 :(得分:0)
由于您的值很稀疏,是否有许多用户要求类似的值?是否在您的应用程序中缓存?请求可以通过作为(x,y)位置函数的散列索引,以便您可以轻松识别位于网格正确区域内的缓存集。例如,将缓存的集存储在树中将允许您快速找到覆盖请求范围的最小缓存子集。然后,您可以对子集执行线性查找,这很小。
答案 5 :(得分:0)
我已经对它做了一些工作,这是一个明显的,O(n ^ 3)贪婪,对称破解算法(记录和字段被单独处理)在类似python的伪代码中。
这个想法很简单:我们首先尝试每个记录一个请求,然后我们做最有价值的合并,直到没有什么值得合并。这个算法有明显的缺点,它不允许重叠请求,但我希望它在现实生活中很好地工作(使用a + n * (p^b) + c * n * p * (1 - g)
成本函数):
# given are # a function cost request -> positive real # a merge function that takes two pairs of sets (f1, r1) and (f2, r2) # and returns ((f1 U f2), (r1 U r2)) # initialize with a request per record requests = [({record},{field if (record, field) is needed}) for all needed records] costs = [cost(request) for request in requests] finished = False while not finished: # there might be something to gain maximum_gain = 0 finished = True this_step_merge = empty # loop onto all pairs of request for all (request1, request2) in (requests x request) such as request1 != request2: merged_request = merge(request1, request2) gain = cost(request1) + cost(request2) - cost(merged_request) if gain > maximum_gain: maximum_gain = gain this_step_merge = (request1, request2, merged_request) # if we found at least something to merge, we should continue if maximum_gain > 0: # so update the list of requests... request1, request2, merged_request = this_step_merge delete request1 from requests delete request2 from requests # ... and we are not done yet insert merged_request into requests finished = False output requests
这是O(n3 * p)因为:
n
次请求while
循环在每次迭代时从池中删除一个请求。内部for
循环迭代(ni^2 - ni
)/ 2个不同的请求对,ni在最坏的情况下从n变为1(当我们将所有内容合并为一个大的时候)请求)。
提前致谢!