1

I have to solve a dependency problem. The situation is the following:

I have a list of packages with their corresponding dependencies as:

pkg1: []
pkg2: [pkg1]
pkg3: [pkg1, pkg2]
pkg4: [pkg1 | pkg 3]
pkg5: [pkg1 | pkg2 | pkg3]

Note that the "|" between two or more dependencies is equivalent to "OR"

My goal is for each package to compute the minimum set of dependencies required to install it.

So for instance:

minimum_set(pkg)

should return

pkg1, pkg2
geek4079
  • 449
  • 2
  • 6
  • 13

2 Answers2

1

Some kind of brute force is unavoidable, since even a very restricted form of this problem is NP-complete.

Specifically, even if the dependency list for all but one of the packages consists of only terms connected by OR, we can trivially reduce the NP-complete Hitting Set problem to it. Suppose we have an instance of Hitting Set, with X the ground set containing elements x_1, ..., x_n, and S the set of k subsets S_1, ..., S_k of X, with S_i \subseteq X for each i, that we want to hit: then for every element x_i in the ground set X, make a package x_i with no dependencies, and for every set S_j \subseteq X, make a package s_j that has as its dependencies all the packages x_k \in S_j, connected by OR operators. Finally make one more "root" package r that has as its dependencies the k packages s_1, ..., s_k, connected by AND. Now, finding minimum_set(r) will find a minimum-size hitting set for the original HS problem -- these are the ground set elements that correspond to the subset of packages x_i that are chosen for installation. So if you can somehow implement minimum_set() in polynomial time, you've solved Hitting Set, and every other NP-complete problem, in polynomial time.

j_random_hacker
  • 47,823
  • 9
  • 95
  • 154
  • can you provide me a general implementation in python of your idea of brute force? – geek4079 May 28 '15 at 22:14
  • No sorry, as I don't know Python. But if you have n packages, you can simply generate all possible 2^n package combinations, test whether each one satisfies all the requirements, and keep the smallest one that does. An optimisation would be to try generating all 1-package combinations first, then all 2-package combinations, and so on, stopping at the first that works. A probably much better optimisation would be to use branch and bound. You could save some time by always forcing in all packages required by required packages with only AND-dependencies. – j_random_hacker May 28 '15 at 22:31
0

Depending on the scale of these packages (i.e. assuming there's not a large amount of permutations that would cause a stack overflow) you could do a recursive look-down each branch to fetch the minimums for each depensency.

Pseudo Code [python-ish style]:

def minimum_set(pkg):
    pkg_dependencies = pkg.dependencies()

    if len(pkg_dependencies) < 1:
        #Package has no dependencies
        return dict(pkg, 0)

    min_dps = []
    for dp in pkg_dependencies:
        if isOR(dp):
            min_dp = min(minimum_set(dp).value()).key()
            min_dps.extend(min_dp)

    return min_dps

That should hopefully return a list like you requested of the minimum amount of dependent packages required for a specific package.

Jakob
  • 1,057
  • 9
  • 20
  • and what about if there are no or dependencies? I think you're not considering that case. Am I right? – geek4079 May 28 '15 at 13:20