解决与依赖关系的简单打包组合

时间:2018-04-20 19:08:52

标签: python algorithm math random combinations

A packing configuration of 7 boxes

这不是一个家庭作业问题,而是来自我正在研究的项目的一些问题。上图是一组盒子的包装配置,其中A,B,C,D在第一层上,E,F,G在第二层上。问题是如果这些方框是以随机顺序给出的,那么这些方框可以放在给定配置中的概率是多少?

放置的唯一条件是所有盒子需要从上到下放置,放置后不能移动。因此,不允许在现有盒子或浮动位置下滑动,但是可以为同一层上的盒子节省空间。例如,E只能在A和B已经到位时放置。如果处理订单是AEB ...那么它不能放在指定的配置中,如果处理顺序是ACBE ...那么E可以正确放置。

更具体的描述是将打包配置转换为一组依赖关系或先决条件。第一层上的ABCD具有0个依赖关系,而E的依赖关系是{A和B},F是{B和C},G是{C和D},相应的依赖关系必须在E或F或G发生之前发生。虽然它不适用于这个问题,但在某些问题中,依赖关系也可以是"或"关系而不是"和"。

我想知道解决这个或一类问题的一般确定性算法是什么?我能想到的一种方法是以随机顺序生成A,B,C,D,E,F,G 10,000次,并且对于每个订单检查是否在调用每个元素时发生了相应的先决条件。然而,这种天真的想法是耗时的,并且不能产生确切的概率(我相信这个问题的答案是基于我实施的这种天真算法的1/18)。

建议将不胜感激!

3 个答案:

答案 0 :(得分:2)

 E F G
A B C D

在您发布的这个特定实例中,每种情况都有两种方式来安排ABECDG,这两个组可以以任何方式交错。

4 * (3 + 4 - 1) choose 3 = 80

现在我们在FB后的任何地方放置C。分析F索引的分布,得到:

{2: 12, 3: 36, 4: 64, 5: 80, 6: 80}

正如你所建议的那样,尝试为这一特定的依赖关系设置一个公式,"凌乱。"在这种情况下,我可能已经生成了交错前两个金字塔,然后计算在每个金字塔中放置F的方法,因为组合解决方案看起来同样复杂。

为了扩展这样的问题,人们可以通过图形搜索以及利用对称性。在这种情况下,从A开始类似于从D开始; BC

Python示例:

nodes = {
  'A': {
    'neighbours': ['B','C','D','E','F','G'], 'dependency': set()},
  'B': {
    'neighbours': ['A','C','D','E','F','G'], 'dependency': set()},
  'C': {
    'neighbours': ['A','B','D','E','F','G'], 'dependency': set()},
  'D': {
    'neighbours': ['A','B','C','E','F','G'], 'dependency': set()},
  'E': {
    'neighbours': ['C','D','F','G'], 'dependency': set(['A','B'])},
  'F': {
    'neighbours': ['A','D','E','G'], 'dependency': set(['B','C'])},
  'G': {
    'neighbours': ['A','B','E','F'], 'dependency': set(['C','D'])}
}

def f(key, visited):
  if len(visited) + 1 == len(nodes):
    return 1

  if nodes[key]['dependency'] and not nodes[key]['dependency'].issubset(visited):
    return 0

  result = 0

  for neighbour in nodes[key]['neighbours']:
    if neighbour not in visited:
      _visited = visited.copy()
      _visited.add(key)
      result += f(neighbour, _visited)

  return result

print 2 * f('A', set()) + 2 * f('B', set()) # 272

# Probability = 272 / 7! = 17 / 315 = 0.05396825396825397

答案 1 :(得分:0)

这可以通过计数方法解决。有两种类型的框:小(S)和大(L)。框中有N个不同的排列,每个框都与一个字符串相关联(例如:ABCDEFGSSSSLLL0000111)。

您可以找到排列的数量,其中包含一些L字母的小数量严格大于大数字。

例如,在SSLSSLL中,当您到达第一个L时,到目前为止有两个S和一个L(所以{{1} }}> S),对于最后两个,L的数量大于S的数量。

答案 2 :(得分:0)

有5040(7!)种可能的排列。排列的数量足够小,以便实现一个脚本,该脚本检查每个排列是否有效"有效"置换(即:可以达到给定的配置)。你可以推断出概率:valid_permutations / all_permutations

现在,问题变成我如何检查排列是否有效?如果我理解正确,当且仅在以下情况下排列有效:

  • E在A和B之后
  • F在B和C之后
  • G在C和D之后

因此代码变为(在0 ... 6中转换A ... B之后):

valids = 0
range7 = range(7)
for perm in itertools.permutations(range7):
    indexes = [perm.index(x) for x in range7]
    if (indexes[4] < indexes[0] or indexes[4] < indexes[1]):
        continue
    if (indexes[5] < indexes[1] or indexes[5] < indexes[2]):
        continue
    if (indexes[6] < indexes[2] or indexes[6] < indexes[3]):
        continue
    valids += 1
print(valids / 5040.)
# 5,39 %!