【问题标题】:Algorithm for optimally choosing actions to perform a task最优选择动作来执行任务的算法
【发布时间】:2011-02-28 09:41:12
【问题描述】:

有两种数据类型:任务和动作。一个动作需要花费一定的时间才能完成,并且该动作由一组任务组成。一个任务有一组动作,我们的工作就是选择其中一个。所以:

class Task { Set<Action> choices; }
class Action { float time; Set<Task> dependencies; }

例如,主要任务可能是“买房子”。此任务的可能操作:“买房子”或“建房子”。 “建造房屋”动作需要 10 小时,并具有“获取砖块”(可能需要 6 小时)和“获取水泥”(需要 9 小时)等依赖项。

总时间是需要执行的所有动作的总和(在本例中为 10+6+9 小时)。我们希望选择总时间最短的动作。

请注意,依赖项可以是菱形的。例如,“获取砖块”可能需要“获取汽车”(运输砖块),而“获取水泥”也需要汽车。即使你做了“拿砖”和“拿水泥”,你也只需要计算得到一辆车所需的时间一次

还要注意,依赖关系可以是循环的。例如“钱”->“工作”->“汽车”->“钱”。这对我们来说没问题,我们只需选择所有“Money”、“Job”和“Car”。总时间就是这三件事的时间之和。

数学描述:

actions 成为选定的操作。

valid(task) = ∃action ∈ task.choices. (action ∈ actions ∧ ∀tasks ∈ action.dependencies. valid(task))
time = sum {action.time | action ∈ actions}
minimize time subject to valid(primaryTask)

我对最佳解决方案感兴趣,但也对近似解决方案感兴趣。也许某种动态编程可以提供帮助?如果问题是树结构的,那么动态规划可以在多项式时间内给出最优解,但菱形结构似乎使问题变得更加困难。如果你有一个算法,但如果有循环它就不起作用,请发布它!我可能仍然可以从中学到很多东西。

方框代表任务,圆圈代表动作(执行动作的时间在圆圈中)。如果该任务是该操作的依赖项,则该操作具有该任务的行。这里再次用图片来描述问题:如果选择了一个矩形(=task),那么必须选择里面的一个圆圈(=actions)。如果选择了一个圆,则必须选择所有个相连的矩形。目标是最小化所选圆圈中的数字总和。

在这种情况下,最佳解决方案是在顶部任务中选择时间为 2 的动作,在底部任务中选择时间为 1 的动作。总时间为 2+1+1=4。在这种情况下,有 2 个最优解。第二种解决方案是在顶部任务中选择时间为 3 的动作,在右下任务中选择时间为 1 的动作。总时间又是 3+1=4。如果我们在顶部任务中选择时间为 3 的动作,我们不必执行左下任务,因为时间为 3 的动作和左下任务之间没有界限。

对于糟糕的绘图,我深表歉意;)还有两个示例(每个示例的最佳解决方案已用蓝色表示,主要任务已用灰色表示):

【问题讨论】:

  • 一个Action如何花费固定的时间(Action.time)?一个 Action 的时间不应该取决于它在 Action.dependencies 中完成每个 Tasks 所需的时间吗?
  • 是的,你是对的,术语很不幸。一个动作所花费的总时间确实是 action.time + 所有依赖项的总时间。 action.time 只是那个动作的时间,不包括依赖的时间。
  • 在问题中,您提到了一些任务只需要执行一次(“获取汽车”)的要求,而我假设每次需要时都必须执行一些任务(“拿砖”?)。 Task 的这个属性在你的数据结构中是如何表示的?
  • 每个任务最多执行一次。如果需要多次执行,则数据结构中有多个副本(例如“为房子 1 获取砖块”和“为房子 2 获取砖块”)。这种情况几乎从来没有发生过。
  • 您如何建模“为房子 1 拿砖”与“为房子 2 拿砖”?

标签: algorithm optimization math computer-science


【解决方案1】:

您可以将此建模为图形并使用shortest path algorithm 来查找解决方案。每个 Tasks 都是一个节点,Actions 是图中的边。

事实上,将节点表示为状态和边表示为状态之间转换所需的动作可能更容易。

如果您将任务视为简单的操作集合,并将节点建模为状态,并将操作建模为这些操作之间的转换。与其将“获得房子”视为首要任务,不如将“开始”和“拥有房屋”视为 2 个节点,将“获得房屋”视为它们之间的过渡。 “Get a house”转换动作可以分解为表示中间状态和动作(即节点和边)的图。您应该能够根据需要将问题分解并从结果图中计算最短路径。

【讨论】:

  • 谢谢。哪些节点之间的最短路径?我假设在主要任务和...之间?
  • 我不明白这是如何工作的,因为通常您需要选择图形的子图,而不仅仅是路径。
  • 这听起来确实像一个可以用图建模的问题,但模型一定比这更复杂。请注意,您必须考虑到“和”要求(建造房屋需要砖和水泥)以及“或”要求(要拥有房屋,您需要建造房屋或由房屋建造),这需要以不同的方式表示。
  • 没错。该图是一种“两级”图,将一个级别的操作与一个级别的任务交替。
  • @sepp2k - 我相信语义是:Task.choices 中的每个动作都是“或”要求,而 Action.dependencies 中的每个任务都是“与”要求。
【解决方案2】:

您可能正在寻找部分订单计划算法:http://blackcat.brynmawr.edu/~dkumar/UGAI/planning.html#algorithms

【讨论】:

  • 这不是完全相同的问题,但我可以使用一些好主意。 mathoverflow 的人指出我的问题是 NP-hard,所以多项式时间解决方案可能太多了;)
【解决方案3】:

我想我很久以前就在PERT图表的背景下这样做了。

这些网络有多大,您需要多久解决一次?在你看到一个之前,你实际上并没有遇到性能问题。

我会使用动态编程。在我从经验中发现共享子任务存在问题之前,我不会认为共享子任务会成为问题。

【讨论】:

  • 结构有时很小,有时很大,有时有数千个甚至数百万个节点,但结构可能很“简单”(例如,大部分是树状的)。不过,共享子任务仍然很常见,因此算法当然需要将这些考虑在内。我仍然想知道可以在小型实例上使用的最佳算法。
  • 顺便说一句,该算法不适用于规划现实世界的人工任务和行动。这将意味着小问题规模。该算法的输入是由其他代码生成的,并且可能很大。而且我确信会发生共享子任务。
  • @Jules:然后我会首先生成一个最坏情况的数据集,并以任何方式对其进行编码,然后从那里开始工作。我学会了不要抽象地处理问题。
  • 如果不知道一个好的算法,那是行不通的,而且我已经知道我可以做一个糟糕的算法。只需遍历所有可能的操作选择,其中 n 个任务有 2^n 个。对于 n = 一百万,这绝对行不通。
  • 另外,在我看来,这个问题并不抽象。条件非常明确,仅此而已。
【解决方案4】:

有过于挑剔的风险,这似乎是一个经典的关键路径方法 (CPM) 问题,而不是 PERT。 PERT 假设每项任务的最差、最佳和平均完成时间,而 Jules 只为每项任务指定一次。也就是说,您可以使用线性规划来查找关键路径并为每个活动生成最早开始时间、最晚结束时间等。 Here's 该方法的有用一页描述。

【讨论】:

  • CTM 是不同的:它的目标是调度(即选择一个顺序)任务,而这个问题是选择采取哪些行动(而不是按什么顺序) .其次,CTM 试图最小化关键路径,而如果任务要由 1 个工人解决,我想最小化总时间。
  • CPM=CTM?在 CPM 的链接中:“a) 关键路径是通过网络的最长路径——它是完成网络的最短时间。这不就是你想要的——尽量减少完成整套活动所需的总时间吗? “CPM调度”实际上是使用CPM作为调度辅助的过程。调度部分的作用是持续使用 CPM 来监控和修改调度的“过程”。
【解决方案5】:

【讨论】:

  • 请详细说明。我不认为拓扑排序与此无关。一方面,图可以是循环的,拓扑排序不会优化任何东西。
  • 嗨。如果你有一个循环图,那么有一个任务 (1) 无法完成,因为它需要另一个任务 (2) 来完成,然后才能完成 (1) ......如果你的图是非循环的您将获得最佳结果,因为您总是选择执行不依赖任何其他任务的任务,并且“关键任务”的顺序是最好的。如果你想让算法更好,你总是可以从没有传入边的所有节点的集合中选择扩展性最小的动作。希望对您有所帮助。
【解决方案6】:

我相信每个可能的执行路径都必须以一个 Task 结束,它的选择集完全由没有依赖关系的 Actions 组成。

如果是这样,那么您可以轻松确定每个此类任务的最短时间。

然后返回到仅依赖于您“解决”的任务的操作,并计算它们的总时间。

通过所有可能的路径向后工作,直到您开始。

运行时间应该是 O(图中的节点数),它应该与生成图所用的运行时间相同。

【讨论】:

  • 问题描述允许循环。
  • 是的,即使不允许循环,我仍然不知道该怎么做。钻石形状是问题所在。给我几分钟。
  • @Nabb 我认为任何不进入无限循环的执行路径都必须有一个结束。
  • mbeckish,您正在考虑执行路径。问题没有指定执行顺序。依赖的任务可以在动作之前或之后执行,没关系。您应该将其视为选择操作的子集,顺序无关紧要。
  • 这是一个你的方法不起作用的例子:i.imgur.com/DjhuO.jpg 抱歉,graphviz 把它颠倒了......最佳解决方案是选择 5 和两个 0 和 0。但是,如果你单独查看连接到主要的两个任务之一,最佳解决方案是选择 4 而不是 0+5。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-08
  • 2016-05-15
  • 2011-07-31
  • 2012-11-11
  • 2010-11-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多