限制条件下的过滤列表

时间:2018-12-25 13:24:06

标签: java algorithm

说明

输入是List<Item>,按得分排序,Item如下:

class Item {
    double score;
    String category; 
    String author;    
    String poi;   
}

现在,在以下限制下,我需要从数组中选择得分最高的 10 元素:

  • 它们应该具有不同的poi
  • 它们应该具有不同的author
  • 同一category中最多有3个项目。并且来自同一category的任何子序列的长度都不应大于2。

如果没有满足上述规则的子序列,则只需返回前10个元素。

我尝试过的

现在,我直接遍历List,并使用三个HashMap<String, Integer>存储每个cagetory/poi/author的外观。然后我用List<Item> selected来存储结果。

  • 如果已经有一个具有此poi的选定元素,则新元素将被丢弃。
  • 如果已经有一个具有此author的选定元素,则新元素将被丢弃。
  • 如果已经选择了三个具有此category的元素,则新元素将被丢弃。
  • 如果selected尾部已经有两个具有此category的元素,则新元素将被丢弃。

问题

当输入较大时,它起作用,但是当输入相对较小时,它不起作用。例如,当输入为

  1. Item1(类别A,作者1)
  2. Item2(类别A,作者2)
  3. Item3(类别A,作者3)
  4. Item4(类别B,作者2)
  5. Item5(类别C,作者5)
  6. Item6(类别D,作者6)
  7. Item7(类别E,作者7)
  8. Item8(类别F,作者8)
  9. Item9(类别G,作者9)
  10. Item10(类别H,作者10)
  11. Item11(类别I,作者11)

那我的解决办法就是

  • Item3被丢弃,因为它与categoryItem1的{​​{1}}相同
  • Item2已丢弃,因为它与Item4具有相同的author
  • 剩下的9个元素。

这不满足Item2。正确的解决方案是丢弃select top 10 elements,并且仅应保留10个元素。

问题

我认为我的解决方案方向错误。因此,我正在寻找其他解决方案来解决此问题。任何产生期望的输出的解决方案都值得赞赏。

3 个答案:

答案 0 :(得分:5)

您使用的原始算法将总是会尽量减少结果数,因为在项目之间的任何互斥选择中,得分最高的项目将获胜。这样,算法就像筛子一样工作,消除了许多得分较低的项目。

为了支持从长度为Y的原始项目集中选择至少一个大小为X(在这种情况下为10)的集合(在您的示例中为11),您需要收集决策集列表,而不是消除项目仅靠分数。决策集(m,n)是m个项的集合,您必须从中选择保留n个项并消除其余项。由于系统中的大多数规则都是属性x的单个项目,因此列表中的大多数决策集将被设置为(m,1)-从m个项目中选择1个,消除其余项。

完整项目集上的第一遍将填充决策集列表,第二遍将遍历该列表并从每个决策集中选择要从原始集中消除的项目。一旦做出决定并将项目从原始集中删除,该决定集将从列表中删除(已解决)。清除决策集列表后,您的原始决策集就合法了。

目标是清除最多Y-X消除中的决策集列表。由于一个项目可以出现在多个决策集中,因此您还可以为每个项目添加“ 生存分数”。生存分数表明如果保留该物品,则必须消除的最大物品数量。通过遍历每个决策集(m,n)并将其添加到包含 m-n 的每个项目的累计得分中,可以对每个项目进行计算。

让我们看看您的示例,并构建其决策集:

  • Item1(类别A,作者1)
  • Item2(类别A,作者2)
  • Item3(类别A,作者3)
  • Item4(类别B,作者2)
  • Item5(类别C,作者5)
  • Item6(类别D,作者6)
  • Item7(类别E,作者7)
  • Item8(类别F,作者8)
  • Item9(类别G,作者9)
  • Item10(类别H,作者10)
  • Item11(类别I,作者11)

我们编译的决策集为(请注意括号中的生存得分):

  • 作者决策集(2,1)= {项目2(2),项目4(1)}
  • 类别决策集(3,2)= {项目1(1),项目2(2),项目3(1)}

我们的目标是在最多1个消除中解决决策集列表。您可以看到,所有项目的生存得分均 1 (意味着保留它们最多将淘汰另外1个项目),而项目2的生存得分为 2 (保留该选项最多可以消除2个项目)。我们负担不起2件商品,因此无论分数如何,我们都无法负担。消除它将解决两个决策集,并且是唯一的选择。

更通用的算法可能更复杂:在每次迭代中,您都淘汰了您无法承受的具有生存分数的项目,如果您没有达到该极限,则使用分数和生存分数的组合来决定必须去哪一个

答案 1 :(得分:2)

您有一个项目列表,您需要删除其中一些项目才能实现目标。您需要检查是否删除每个候选项目可以为您提供更好的解决方案!尝试删除每个可以改善列表的项目,然后查看您是否已实现目标。

以下是递归解决的步骤:

  1. 如果给定列表大小小于或等于10,则返回
  2. 构建要从列表中删除的候选列表(在示例中为Item1,Item2,Item3和Item4)。如果候选列表为空,则操作完成。
  3. 逐个删除每个候选人,调用递归,然后放回已删除的项目
  4. 如果递归返回true,则说明您已完成

这是一些伪代码

bool recurse(list l)
  if (l.size() <= 10) {
    // cannot remove anything
    return false
  candidates = buildCandidateList(l)
  if (candidates.size() == 0)
    // check for top 10 items
    return true
  for each c in candidates {
    remove c from l
    if (recurse(l) == true) 
      // check for top 10 items
      return true
    add c back into l
  }
// just get first 10 items
return false

此解决方案基于递归的回溯。另外,我认为也有可能建立自底向上的动态编程解决方案,这将在Big-O复杂性时间方面带来更好的结果。

答案 2 :(得分:2)

我认为您的解决方案相当不错,更不用说高效了,但是,您没有选择最好的解决方案。在这种情况下,只有在选择一项时会导致前10名列表中的项少于10条,这种情况才会发生。

决策集(由@Assafs建议)可以解决问题;但是,对于您的问题的一般情况,通用决策集算法非常无效。如果您必须为数百万个条目建立完整的决策集,只是为了避免输入项不足的情况,对我来说似乎已经过头了。

此外,“前10名”结果集的描述并不能揭示在所有情况下正确的结果集。例如,如果您在示例中提供了更多项目,则没有任何迹象表明删除项目2是正确的方法。尚不清楚我们应该如何衡量“十大”中的十个。

我建议在您的算法中添加部分决策过程,这样一方面不会损害性能,另一方面将提供解决短输入问题的方法。

要实现此目的,您需要保留每个已选择项目到导致其不合格的项目的地图。最重要的是,您需要保留每个不合格项目到不合格项目的地图。
从贪婪算法中获得结果集后,如果结果少于10个,则可以遍历结果集,并用不合格的项目替换不合格的项目。
决定首先检查哪些项目的方式实际上取决于您,因为再次重申,没有关于如何确定“前十名”的描述。您可以先删除得分低的项目,这些项目不符合其他1个项目的资格,或者寻找得分最高的项目,等等。
重要的是,无论您选择哪种算法,结果集最多可包含10个项目,因此,即使您提出了O(n ^ 3)顶级算法,它仍然是恒定的1000次运算算法实际上是O(1)。 在执行初始算法的过程中,构建实现此目标所需的地图也需要O(1)时间。