Question

Say, we have some items, and each defines some partial sorting rules, like this:

I'm A and I want to be before B

I'm C and I want to be after A but before D

So we have items A,B,C,D with these rules:

  • A>B
  • C<A, C>D
  • nothing else! So, B and D have no 'preferences' in ordering and are considered equal.

As you see, transitive relation rules are not working here. However, if A>B it still means that B<A. So, there can be multiple possible results of sorting:

  1. A B C D
  2. A C D B
  3. A C B D
  4. A B C D

How can I implement a sorting algorithm that handles such a situation?


The reason: there're multiple loadable modules, and some of them 'depend' on others in a way. Each module can declare simple rules, relative to other modules:

Load me before module A

Load me after module B

Load me before module A but after module B

now I need to implement this ordering somehow.. :)


Answer: code by Paddy McCarthy (MIT)

## {{{ http://code.activestate.com/recipes/577413/ (r1)
try:
    from functools import reduce
except:
    pass

data = {
    'des_system_lib':   set('std synopsys std_cell_lib des_system_lib dw02 dw01 ramlib ieee'.split()),
    'dw01':             set('ieee dw01 dware gtech'.split()),
    'dw02':             set('ieee dw02 dware'.split()),
    'dw03':             set('std synopsys dware dw03 dw02 dw01 ieee gtech'.split()),
    'dw04':             set('dw04 ieee dw01 dware gtech'.split()),
    'dw05':             set('dw05 ieee dware'.split()),
    'dw06':             set('dw06 ieee dware'.split()),
    'dw07':             set('ieee dware'.split()),
    'dware':            set('ieee dware'.split()),
    'gtech':            set('ieee gtech'.split()),
    'ramlib':           set('std ieee'.split()),
    'std_cell_lib':     set('ieee std_cell_lib'.split()),
    'synopsys':         set(),
    }

def toposort2(data):
    for k, v in data.items():
        v.discard(k) # Ignore self dependencies
    extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys())
    data.update({item:set() for item in extra_items_in_deps})
    while True:
        ordered = set(item for item,dep in data.items() if not dep)
        if not ordered:
            break
        yield ' '.join(sorted(ordered))
        data = {item: (dep - ordered) for item,dep in data.items()
                if item not in ordered}
    assert not data, "A cyclic dependency exists amongst %r" % data

print ('\n'.join( toposort2(data) ))
## end of http://code.activestate.com/recipes/577413/ }}}
Was it helpful?

Solution

You'll want to construct a dependency graph (which is just a flavor of directed graph), and then follow a topologically sorted ordering. It's been a while since I took a combinatorics class, so the Wikipedia article will probably be more helpful than I am for a topological sort algorithm. I'm hoping giving you the proper terminology is helpful. :)

As far as constructing the graph, you'll basically just need to have each module with a list of that module's dependencies.

You'll just need to rephrase your rules a bit... "I'm C and I want to be after A but before D" would be expressed as "C depends on A" as well as "D depends on C", such that everything is flowing in a standard direction.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top