【问题标题】:Algorithm to find the best combination of items under certain constraints在某些约束下找到最佳项目组合的算法
【发布时间】:2012-01-19 12:55:48
【问题描述】:

我会尝试用数学语言来解释这个问题。
假设我有一组项目X = {x_1, x_2, ..., x_n}X 的每一项都属于集合S_1, S_2, ..., S_5 之一。我认为X 的所有子集由 5 个项目组成: {x_i1, x_i2, ..., xi5} 所以x_i1 属于S_1,...,x_i5 属于S_5
一些子集被认为是正确的,而一些子集被认为是不正确的。如果子集不包含冲突项,则认为子集是正确的。我有一个函数 f1 来确定一对项目是否冲突。
我还有一个函数 f2,它可以比较这些正确的子集并说出哪个子集更好(它们也可能相等)。
我需要找到最好的不冲突的子集。

我使用的算法:
我构建了所有子集,丢弃了不正确的子集。然后我使用 f2 作为排序函数对正确的子集进行排序,并首先选择最佳子集(我使用了快速排序算法)。就存在大量子集而言,此过程花费的时间不足。

在时间消耗方面有更好的方法吗?

更新
让我们将 x_i 视为具有整数端点的区间。如果 2 个区间不相交, f1 返回 true,否则返回 false。 f2 比较子集中区间的总和长度。

【问题讨论】:

  • 如果没有更多关于用于对子集进行排名的函数的详细信息,我认为不可能做得更好;考虑一个函数,它赋予除一个子集之外的所有内容的权重 0,该子集的权重为 1。找到该子集的唯一可能方法是将它们全部列出,除非您已经对该函数有所了解。
  • 我猜 f2 与 f1 有某种关系
  • 我更新了帖子,让问题更清楚了。
  • 首先,开始优化,你需要弄清楚,哪个部分花费的时间最多。制定基准并更新问题。
  • 我在这里需要的不是微优化,而是另一种解决相同问题的方法。所以我认为没有必要进行分析。

标签: algorithm


【解决方案1】:

这个问题是最大加权间隔调度算法的变体。 DP算法的多项式复杂度为O(N*log(N))O(N)空间用于天真问题,O(2^G * N * logn(N))复杂度和O(2^G * N)空间用于这个变化问题,其中GN表示组的总数/子集(这里是 5 个)和间隔。

如果 x_i 不代表区间,那么问题出在 NP 上,其他解决方案已经证明了这一点。

我先解释一下最大加权区间调度的动态规划解,然后解决变分问题。

  • 我们得到了区间的起点和终点。分别设start(i)end(i)weight(i)为区间i的起点、终点、区间长度。
  • 根据起点的升序对区间进行排序。
  • 让区间的排序顺序为1, 2, ... N
  • next(i)代表下一个不与区间i重叠的区间。
  • 让我们将子问题S(i) 定义为仅考虑作业i, i+1, ... N 的最大加权区间。
  • S(1) 是解决方案,它考虑来自 1,2,... N 的所有作业并返回最大加权间隔。
  • 现在让我们递归定义S(i)

.

S(i)  = weight(i)                             if(i==N) // last job
      = max(weight(i)+S(next(i)), S(i+1)

此解决方案的复杂性为O(N*log(N) + N)N*log(N) 用于查找所有工作的 next(i)N 用于解决子问题。空间是O(N),用于保存子问题解决方案。

现在,让我们解决这个问题的变体。

  • 让我们一起看看 X 中的所有区间。每个区间都属于集合 S_1、... S_5 之一。
  • 分别设start(i)end(i)weight(i)subset(i)为起点、终点、区间长度、区间i的子集。
  • 根据起点的升序对区间进行排序。
  • 让区间的排序顺序为1, 2, ... N
  • next(i)代表下一个不与区间i重叠的区间。
  • 让我们将子问题 S(i, pending) 定义为最大加权区间,仅考虑作业 i, i+1, ... Npending 是一个子集列表,我们必须从中选择一个区间。
  • S(1, {S_1,...S_5}) 是解决方案,它考虑所有工作 1,...N ,为每个 S_1,...S_5 选择一个区间并返回最大加权区间。
  • 现在让我们递归定义S(i),如下所示。

.

S(i, pending)  = 0                          if(pending==empty_set) // possible combination
               = -inf                       if(i==N && pending!={group(i)}) // incorrect combination
               = S(i+1, pending)            if(group(i) not element of pending)
               = max(weight(i)+S(next(i), pending-group(i)),
                     S(i+1, pending)

请注意,我可能遗漏了一些基本情况。

这个算法的复杂度是O(2^G * N * logn(N))O(2^G * N) 空格。 2^G * N 代表子问题大小。

作为估计,对于 G<=10 的小值和 N>=100000 的高值,此算法运行得非常快。对于G>=20 的中等值,N<=10000 也应该很低,这样算法才能收敛。对于 G>=40 的高值,算法不会收敛。

【讨论】:

  • 非常感谢您的回答。似乎它会为我工作。在接受你的回答之前我会先试一试。
【解决方案2】:

在没有进一步限定域和评估函数的情况下,通过将 SAT 归约到它上,可以很容易地证明这个问题是 NP 完全的(即让 S_1,...,S_5 为 {true,false} 并且 f2 = 1 如果公式是完整的,如果不是,则为 0)。因此,在这种情况下,即使不考虑 f1,您也不走运。

如果你对 f1 和 f1 的实际结构有更多的了解,你可能会有更多的运气。查看Constrait Satisfaction Problems,了解在 f1 和 f2 的结构中要查找的内容。

【讨论】:

  • 感谢您的回答。我更新了帖子,使 f1 和 f2 的含义变得更加清晰。
  • 您能详细说明一下吗?我看不到如何将具有 n 个变量的布尔表达式简化为 5 个变量 S_1、...、S_5。此外,您必须考虑 f1,这个问题的算法可能会使用它的结构来快速找到解决方案。你将如何构建 f1?
  • @Ishtar:我首先处理的是一般情况(不限于5个变量,不考虑f_2的结构)。如果您需要枚举 n 个变量的所有内容,则需要枚举 5 个变量的所有内容。然而,对于 5 个变量,一般问题已经是 NP-Complete,这可以通过使用 S_i = {true,false}^(m_i) 和 sum(m_i) = n 轻松显示。现在,您可以在此问题中使用 n 个变量对任何公式进行编码。 f_1 的定义也很简单:f_1 = no_conflict 任意两个元素。对于给出 f_2 结构的更具体的部分,我不知道。
【解决方案3】:

让我们将 x_i 视为具有整数端点的区间。如果 2 个区间不相交, f1 返回 true,否则返回 false。 f2 比较子集中区间的总和长度。

如果我理解正确,这意味着我们可以为 X 中的每个 x_i 分配一个值(其长度)。那么就不需要对每个可能的解决方案/子集评估 f2。

最小的 5 x_i 不太可能形成最佳子集。根据实际数据,最佳子集可能是 5 个最大间隔。所以我建议按值对 X 进行排序。一般的想法是从最高的 x 开始并尝试添加更多的 x(首先是最高的),直到你得到 5 个不重叠。您很可能会在生成所有可能子集的一小部分之前找到最佳子集(当然取决于具体问题)。但在最坏的情况下,这并不比您的解决方案快。

【讨论】:

  • 我认为它可能有效。您能否提供更多关于总体思路的详细信息?
【解决方案4】:

如果我们抛开从每个 S_i 中取一个 x 的条件,这个问题等价于区间图中的最大权重独立集(即在一个图中找到一个最大权重的成对不连通顶点集,其中顶点表示区间,如果对应区间重叠,则顶点相连)。这个问题可以在多项式时间内解决。这里的版本也有每个顶点的颜色,选择的顶点需要有所有不同的颜色。我不知道如何在多项式时间内解决这个问题,但是您可以利用没有太多颜色的事实:制作一个动态规划表 T[C, x],其中 C 是一组颜色,x 是位置区间的端点。 T[C, x] 应该包含您可以从 |C| 获得的最大权重与 C 中位于 x 左侧的颜色的间隔。然后,您可以从左到右填写表格。这应该是可行的,因为只有 2^5=32 个颜色集。

【讨论】:

    【解决方案5】:

    如果我对您的问题的理解是正确的,我有一个很好的解决方案: 所以我从我理解的开始

    each Integer is actually an interval from I1 to I2 and a Set is a 
    combination of such intervals. A Set is correct if none of the intervals 
    are intersecting and Set1>Set2 if the sum of Intervals in S1> sum of Intervals in S2.
    

    所以在这种情况下我会做的事情就是这样。

    1. 在比较区间以确定它们是否相交时,请执行此操作。

      a) 按起点顺序对区间进行排序

      b) 比较连续间隔的第一个终点和起点以确定重叠。保留一个名为gap的整数,如果2个区间的开始和结束不与它们的差值重叠增量gap。

    这将通过 Endpoint(lastI)-Startpoint(firstI) - Gap 自动为您获取集合中的间隔总和。

    => 如果您只需要最好的,您可以取一个变量最大值,并在出现时继续比较集合。

    =>如果你需要top5之类的就跟着下面,否则跳过。

    1. 一旦你得到总和并且集合是正确的,将总和添加到 5 个元素的“MinHeap”中。前 5 个元素将保持原样。基本上,您正在跟踪前 5 个元素。当一个新集合小于堆的最小值时“什么都不做并忽略这个集合,因为它小于前 5 个集合”,当集合大于最小值时(意味着它在前 5 个)替换最小值并向下筛选元素,将前 5 名的最小值保持在顶部。这将始终保留堆中的前 5 个元素。

    2. 现在您已经有了前 5 个元素,您可以轻松地确定具有 5 个 pop 的最佳元素。 :)

    注意:如果间隔是随机顺序的,它将使您进入 O(n^2) 解决方案,然后每个比较将再次有 4 个 if 语句来检查重叠位置。您可以对 O(nlogn) 中的间隔进行排序,然后遍历列表一次以确定重叠,(nlogn +n = nlogn) 同时获得前 5 个集合。这应该会提高你的表现和时间。

    .

    【讨论】:

      【解决方案6】:

      我没有得到答案,因为你问了非常抽象的问题,但我会给你一个想法。

      尝试考虑多线程。例如,您可以创建一个线程数有限的线程池。然后找到一个递归解决方案,并在您深入其中时为每个循环启动新任务。

      我是说你可以将这个问题分解为许多小任务,因为你的算法会更好。

      有问题地思考而不是数学思考!

      【讨论】:

        【解决方案7】:

        考虑使用查找表来优化 f1 的时间。考虑将您发现的子集插入到合并排序列表中,而不是在最后进行快速排序。如果域很小且有限,您可以通过填充稀疏数组来实现一些非常快速的合并排序。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-08-26
          • 1970-01-01
          • 1970-01-01
          • 2010-12-27
          • 1970-01-01
          • 1970-01-01
          • 2012-10-11
          • 1970-01-01
          相关资源
          最近更新 更多