寻找最佳分组的算法

11

一个设备包含一组位置,其中一些位置包含我们想要定期读取的值。

我们想要定期读取的位置列表还指定了我们想要多久读取一次。可以更频繁地读取值,但不能更少地读取。

单个读取操作可以从数组中读取连续的位置序列,因此可以从一个读取操作中返回多个值的组。可以在单个操作中读取的最大连续位置数为M。

目标是将位置分组,以使平均时间读取操作的数量最小化。如果有多种方法可以做到这一点,则打破平局的方法是使平均读取位置数最小。

(如果实现此操作的算法允许对位置列表进行增量更改-即添加或删除一个位置/从列表中不需要从头重新计算分组!则会获得额外的奖励分数)

我将尝试通过一些示例来澄清M = 6的情况。

以下图表显示位置数组。数字表示该位置的所需读取周期。

| 1 | 1 |   |   | 1 |   |   |   |   |   | 5 |   | 2 |
\-------------------/                   \-----------/
     group A                               group B

在这个第一个示例中,A组每秒读取一次,B组每2秒读取一次。需要每5秒读取的位置实际上每2秒就被读取了-这是可以接受的。
| 1 |   |   |   |   | 1 | 1 |   | 1 |
\-----------------------/\----------/
         group A            group B         (non-optimal!)

这个例子展示了我最初的简单算法失败的情况,即先填满第一组,然后再开始另一组。下面的分组更加优化,因为虽然每秒钟读取组数相同,但在这些组中读取的位置数量更少:
| 1 |   |   |   |   | 1 | 1 |   | 1 |
\---/               \---------------/
group A                  group B            (optimal)

最后,举一个三组比两组更好的例子:

| 5 |   |   |   |   | 1 | 1 |   |   |   |   | 5 |
\-----------------------/\----------------------/
        group A                  group B    (non-optimal)

这个解决方案每秒需要进行两次组读取。更好的解决方案如下:

| 5 |   |   |   |   | 1 | 1 |   |   |   |   | 5 |
\---/               \-------/               \---/
group A              group B               group C

这需要每5秒进行两次读取(A组和C组),加上每秒一次的读取(B组):每秒1.4次读取组。
编辑:(如果允许读取是非周期性的,则此示例有更好的解决方案。在第1秒,读取第一个解决方案的两个组。在第2、3、4和5秒读取第二个解决方案的B组。重复。这将导致每秒1.2个组读取。但我要禁止这样做,因为它会使代码对调度读取负责变得更加复杂。)
我查了聚类算法,但这不是聚类问题。我还找到分配数字列表给N组的算法在某些条件下, 它指向了“装箱”问题,但我不认为这就是问题所在。
顺便说一句,抱歉标题含糊不清。我想不出简洁的描述,甚至相关的搜索关键字! 新增示例,于2010年9月28日添加: 这与前面的示例相似,但所有项都以相同的速率更新。现在两个组比三个组更好:
| 1 |   |   |   |   | 1 | 1 |   |   |   |   | 1 |
\-----------------------/\----------------------/
        group A                  group B          (optimal)

我已开始尝试着了解如何实现迭代改进。假设一个分组算法得出了以下结果:

{{

| 1 |   |   |   |   | 1 | 1 |   |   |   |   | 1 | 1 |   |   |   |   | 1 |
\---/               \-------/               \-------/               \---/
group A              group B                 group C               group D  (non-optimal)
\-----------------------/\----------------------/\----------------------/
        group A                  group B                  group C           (optimal)

这可以改进为三个相邻的6个组。Rex建议(下面的评论)我可以尝试将三个一组合并成对。但在这种情况下,我必须将四重奏合并为三重奏,因为没有合法的中间排列可以使A+B+C(或B+C+D)重新排列成一对,并将D保留不变。
最初,我认为这表明通常情况下不能保证通过进行局部修改从现有有效解决方案创建新的有效解决方案。这意味着可以使用模拟退火、遗传算法等算法来尝试优化次优解。
但是Rex指出(下面的评论)您始终可以将现有组分成两个。尽管这总是增加成本函数,但这只意味着解决方案需要摆脱其局部最小值才能达到全局最小值。

我本来想说,你总是可以将具有相同时间轴的相邻项进行分组(例如你最后一个示例中的B组),但在涉及到M时可能并非如此。不过,这指向了一种使用明智规则逐步构建越来越大的组的策略。你的问题规模有多大? - phkahler
这听起来可能是一个在理论最优情况下 NP-完全或者至少是 NP-难问题。您可以尝试重新发布到 CS Theory Stack Exchange - Daisy Sophia Hollman
@phkahler:在我的情况下,M大约是100,数组大小可以是1到30000左右的任何值,但通常只有几百个,所需位置的数量可以是1到100左右的任何值。 - Ian Goldby
我怀疑这可能是一个NP-Hard问题。如果是这样,只要没有使其严重失败的病态情况,近似最优解就可以了。如果在几天内没有得到满意的答案,我会尝试在CS Theory Stack Exchange上提问。 - Ian Goldby
抱歉挑剔一下,但是你第一个示例的解决方案并不是最优的(至少我是这样理解规则的)。在这种情况下,读取整个数组会更优,因为每秒只需进行1次读取。 - user180326
@jdv:这些示例都是针对M=6的情况 - 也就是说,单个组不能覆盖超过6个位置,并且它们都必须相邻。 - Ian Goldby
1个回答

4
这个问题和类似的NP完全问题一样,在添加新项时具有不稳定性,因此我认为它也是一个NP完全问题。由于我猜想您希望得到一个相对可行的解决方案,而不是一个难度证明,所以我将专注于提供一种近似解的算法。
我会通过将其转换为一个图来解决这个问题,其中如果必须每秒读取N次,则箱子的价值为1 / N,并使用宽度为M(例如6)的模糊图形,峰值在原始值处。 (对于6,我可能会使用加权(1/6 1/5 1/4 1/3 1/2 1 1/2 1/3 1/4 1/5 1/6)。)然后将箱子扔到所有局部最大值处(如果可以的话,按距离排序对并首先覆盖接近的最大值对)。现在,您将覆盖大多数最重要的值。然后通过扩展现有读取或必要时添加新读取来捕获任何缺失的组。根据结构,您可能需要通过在读取之间移动位置来添加一些细化,但如果您很幸运,甚至可能不需要这样做。
由于这本质上是一种局部算法,如果您跟踪模糊图形,您可以相当容易地添加新项并在本地重新执行峰值覆盖(以及本地细化)。
只是为了看看这将如何处理您的数据,两组情况将如下所示(乘以60,因此我不必跟踪分数权重)。
 60 30 20 15 12 10 00 00 00   <- contribution from left-most location
 10 12 15 20 30 60 30 20 15   <- second
 00 10 12 15 20 30 60 30 20   <- third
 00 00 00 10 12 15 20 30 60   <- rightmost
 --------------------------
 70 42 47 50 74 B5 B0 80 95   (using "B" to represent 11)
 ^^             ^^       ^^   Local maxima
   -------------  -------
     dist=6        dist=4
               |===========|  <- Hit closely-spaced peaks first
|==|                          <- Then remaining

我们完成了,解决方案是最优的。

对于三组示例,将“5”加权为“1/5”,并将所有内容乘以300,以避免出现分数。

060 030 020 015 012 010 000 000 000 000 000 000   <- from 5 on left
050 060 075 100 150 300 150 100 075 060 050 000   <- 1 on left
000 050 060 075 100 150 300 150 100 075 060 050   <- on right
000 000 000 000 000 000 010 012 015 020 030 060   <- 5 on right
-----------------------------------------------
110 140 155 190 262 460 460 262 190 155 140 110
                   |=======|                      <- only one peak, grab it
===                                         ===   <- missed some, so pick them back up

好的,我现在回来了。不幸的是,如果所有项目以相同的速率更新,上述算法在后一个例子中会失败 - 你会得到相同的三个组,而实际上应该只有两个组。我正在尝试调整算法来修复这个问题,但似乎从根本上来说很难使其更倾向于较大的组,以满足基本上是非局部标准(最小化组数)的要求。 - Ian Goldby
@Ian - 任何本地更改都可以传播到整个解决方案的属性是所有NP类型问题所具有的。有两种方法可行:(1)穷举搜索(当然要巧妙地处理;例如,如果有一段0太宽以至于不能由单个读取跨越,则将问题分成两个子问题),和(2)近似启发式算法,平均情况下工作得相当好,但在特定的问题情况下必然失败。我的解决方案属于第二种类型。 - Rex Kerr
我想说的是,在我研究这个问题时,我看过的所有NP类型问题中,你可以对解决方案进行小的本地更改,它仍然是一个有效的解决方案,尽管可能不太优化,例如在旅行商问题中交换两个城市。但在这里的最终示例中,我无法进行任何本地修改,以便得到仍然有效的解决方案。不过我正在重新评估这个问题——如果我能找到一种方法来表示解决方案,使得无效的解决方案没有表示,那么本地修改将始终是可能的。不确定这是否可行。 - Ian Goldby
@Ian - 你只是在考虑错误的本地修改类型。你总是可以将一个读取拆分为两个,这是一种本地修改。但是,这种本地修改不会轻易地带领你找到全局解决方案--你被困在了局部最小值中。而其他问题--例如旅行商问题--也完全相同。你可能会陷入一个好但次优的路线上,必须重新设计一切才能走上真正最短的路径。无论如何,如果问题很小且最优性至关重要,请进行暴力搜索。否则,也许你可以容忍一些低效率。 - Rex Kerr
我想我应该提到,尽管消除了非有效的分组,但问题的规模仍然会“组合地”增加,因此当N变得相当大时,它仍然变得不可行。 - Ian Goldby
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接