【问题标题】:Prints a minimal set of packages required to install the package打印安装包所需的最小包集
【发布时间】:2015-05-27 02:57:52
【问题描述】:

我必须创建一个数据结构来表示包之间的依赖关系。

我以为我可以简单地使用图表,但问题是某些包可能依赖于“可选”包之一,我们必须选择这些最佳包中的哪一个更方便安装(基本上,从这些可选的选择,我们需要安装最好的)。

例如,假设我有以下情况:

  1. 包1:
  2. 包2:包1
  3. 包 3:包 1、包 2
  4. 包4:包1 |包3
  5. 包5:包1,包2 |包3

这种情况意味着:

  1. 包 1 没有依赖项。
  2. 包2依赖包1(我们需要安装包1)。
  3. 包 3 依赖包 1 和 2(我们需要同时安装)
  4. 包 4 依赖包 1 或 3(我们可以安装 1 或 3,但我们需要选择最佳选择,这意味着选择 包 4 依赖较少的包)
  5. 包 5 依赖于包 1,它也依赖于包 2 或 3(同样,我们需要选择最佳选择)

现在,问题显然是我们何时可以在不同的包之间进行选择。

我们如何选择它们?

例如,为什么包 4 应该依赖包 1 而不是 3?

我们可以尝试检查包 1 和包 3 依赖于哪些包,但是如果我们有 10000 个选择,但我们只需要 1 个最佳选择呢?它需要数千个循环和过于复杂的东西。可能有一些简单的东西,但我不知道是什么。

这种选择安装哪个更好的尝试似乎导致了递归算法,这已经让我大吃一惊了。

【问题讨论】:

    标签: algorithm data-structures graph dependencies packages


    【解决方案1】:

    您可以创建具有两种类型节点的有向图:

    • package 节点,其子节点使用 AND 逻辑组合
    • OR 节点,其子节点使用 OR 逻辑组合

    从一个节点开始的所有弧都代表它的依赖关系。

    对于您的示例,这将创建以下图表:

    如果您必须评估一个包的最小依赖集,您可以使用深度优先搜索算法访问该图:

    • 当您访问 package 节点时,您会对其子节点的依赖数求和
    • 当您访问 OR 节点时,您会考虑其子节点的最小依赖项数

    示例代码

    这是一个如何在 python 中实现它的示例(我使用了 python,因为它的语法几乎像伪代码;你应该能够理解它的要点):

    #!/usr/bin/env python3
    
    class Package:
        # Constructor
        def __init__(self, name, dependencies):
            self.name = name
            self.dependencies = set(dependencies)
        # Needed to print the package
        def __str__(self):
            return self.name
        # This method returns the optimal set of dependencies for this package
        def getOptimalDependencies(self):
            optimalDependencies = set()
            for dependency in self.dependencies:
                optimalDependencies = optimalDependencies.union(dependency.getOptimalDependencies())
            optimalDependencies.add(self)
            return optimalDependencies
    
    class Or:
        # Constructor
        def __init__(self, dependencies):
            self.dependencies = set(dependencies)
        # This method returns the optimal set of dependencies
        # of the packages combined with this 'OR'
        def getOptimalDependencies(self):
            optimalDependencies = set()
            for dependency in self.dependencies:
                alternativeDependencies = dependency.getOptimalDependencies()
                if len(optimalDependencies) == 0 or len(alternativeDependencies) < len(optimalDependencies):
                    optimalDependencies = alternativeDependencies
            return optimalDependencies
    

    然后您可以将示例的包创建为:

    package1 = Package("package1", [])
    package2 = Package("package2", [package1])
    package3 = Package("package3", [package1, package2])
    package4 = Package("package4", [Or([package1, package3])])
    package5 = Package("package5", [package1, Or([package2, package3])])
    

    要获取package5 的最佳依赖项列表,您可以调用package5.getOptimalDependencies()

    如果我们打印它:

    print(','.join(map(str, package5.getOptimalDependencies())))
    

    我们得到:

    package2,package1,package5
    

    如果你有依赖循环,你必须为此插入一些控制。

    【讨论】:

    • 我在 python 中添加了一些代码来说明这个想法。然后,您可以用您选择的语言重新实现。该算法可以通过避免在每次访问其节点时重新计算包的依赖项来改进,但我选择保持代码简单,以便它更具可读性
    • 为什么getOptimalDependencies方法要找到最短路径?为什么我不明白为什么......
    • 你可以通过归纳来证明。您知道每个叶节点的一组最佳依赖项(那些没有传出弧,因此没有依赖项)。如果您知道给定节点的每个子节点的最佳依赖关系,那么您就知道该节点的最佳依赖关系:对于 package 节点,它将是其子节点的依赖关系的并集,对于 OR 节点,它将是其子项中最小的依赖集。我们基本上是从叶子开始访问图,然后一直到要查找其依赖关系的节点。
    • 您的功能有问题(一般解决方案)。假设我们有以下情况:A: B | C, D | EB: G, H, MC: K, ND: G, H, ME: L, J。如果我们运行您的算法,它将为包 A 返回错误的答案,因为您的算法实际上在本地返回最佳选择(当访问单个节点和 OrNodes 时)并且没有考虑到 @987654341 的整体状态@。也许我们需要一些检查...
    【解决方案2】:

    我们可以将其建模为一个伪布尔优化问题。

    假设有k 包。定义 k 个布尔变量 x_i,其中 x_i 为真当且仅当最终选择是安装 package_i。因此,目标是最小化x_i 的总和,其中 1 x_i 的加权总和。

    为了对约束“package5: package1, package2 | package3”建模,我们可以发现“package5 is installed”暗示“package1 is installed”。如果伪布尔优化器支持Conjunctive normal form中表示的约束,那么它可以写成

    (!x_5 + x_1) 
    

    如果只允许线性不等式,那么它是

    (1 - x_5) + x_1 >= 1
    

    另一半,“package5 is installed” 暗示“package2 and/or package3 is installed”,可以写成

    (!x_5 + x_2 + x_3)
    

    (1 - x_5) + x_2 + x_3 >= 1
    

    最后,你必须断言你需要的包。

    x_k
    

    x_k = 1
    

    现在我们已经将问题建模为伪布尔优化问题,您可以调用任何可用的求解器并对其进行求解。您可以参考Pseudo Boolean Competition 了解最先进的求解器。

    【讨论】:

      猜你喜欢
      • 2012-01-10
      • 1970-01-01
      • 2011-01-17
      • 1970-01-01
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      • 2017-10-10
      • 1970-01-01
      相关资源
      最近更新 更多