Trouver en python des combinaisons d'ensembles mutuellement exclusifs à partir des éléments d'une liste

dans un projet sur lequel je travaille actuellement, j'ai mis en œuvre environ 80% de ce que je veux que mon programme fasse et je suis très satisfait des résultats.

<!-Dans les 20% restants, je suis confronté à un problème qui me laisse un peu perplexe sur la façon de le résoudre. Ici, c'est:

j'ai trouvé une liste de listes qui contiennent plusieurs nombres (longueur arbitraire) Par exemple:

listElement[0] = [1, 2, 3]
listElement[1] = [3, 6, 8]
listElement[2] = [4, 9]
listElement[4] = [6, 11]
listElement[n] = [x, y, z...]

où n peut atteindre jusqu'à 40 000.

en supposant que chaque élément de liste est un ensemble de nombres (dans le sens mathématique), ce que je voudrais faire est de dériver toutes les combinaisons d'ensembles mutuellement exclusifs; c'est-à-dire, comme le powerset des éléments de liste ci-dessus, mais avec tous les éléments non-disjoint-ensemble exclus.

donc, pour continuer l'exemple avec n=4, j'aimerais trouver une liste qui a les combinaisons suivantes:

newlistElement[0] = [1, 2, 3]
newlistElement[1] = [3, 6, 8]
newlistElement[2] = [4, 9]
newlistElement[4] = [6, 11] 
newlistElement[5] = [[1, 2, 3], [4, 9]]
newlistElement[6] = [[1, 2, 3], [6, 11]]
newlistElement[7] = [[1, 2, 3], [4, 9], [6, 11]]
newlistElement[8] = [[3, 6, 8], [4, 9]]
newlistElement[9] = [[4, 9], [6, 11]

un cas invalide, par exemple une combinaison [[1, 2, 3], [3, 6, 8]] parce que 3 est commun en deux éléments. Être il n'y aucune manière élégante de le faire? Je vous serais extrêmement reconnaissant pour tous vos commentaires.

<!-Je dois aussi préciser que je ne voudrais pas faire la fonction powerset, parce que la liste initiale pourrait avoir un assez grand nombre d'éléments (comme je l'ai dit n pourrait aller jusqu'à 40000), et prendre le powerset avec autant d'éléments ne serait jamais terminé.

8
demandé sur knedas 2012-11-27 00:23:45

6 réponses

la méthode utilisée dans le programme ci-dessous est similaire à quelques réponses précédentes en excluant les ensembles non disjoints et donc généralement pas tester toutes les combinaisons. Il diffère des réponses précédentes en excluant avec gourmandise tous les ensembles qu'il peut, aussi tôt que possible. Cela lui permet de fonctionner plusieurs fois plus rapidement que la solution de NPE. Voici une comparaison de temps des deux méthodes, en utilisant des données d'entrée avec 200, 400,... 1000 tailles-6 ensembles ayant des éléments dans la gamme de 0 à 20:

Set size =   6,  Number max =  20   NPE method
  0.042s  Sizes: [200, 1534, 67]
  0.281s  Sizes: [400, 6257, 618]
  0.890s  Sizes: [600, 13908, 2043]
  2.097s  Sizes: [800, 24589, 4620]
  4.387s  Sizes: [1000, 39035, 9689]

Set size =   6,  Number max =  20   jwpat7 method
  0.041s  Sizes: [200, 1534, 67]
  0.077s  Sizes: [400, 6257, 618]
  0.167s  Sizes: [600, 13908, 2043]
  0.330s  Sizes: [800, 24589, 4620]
  0.590s  Sizes: [1000, 39035, 9689]

Dans les données ci-dessus, la colonne de gauche montre les temps d'exécution en secondes. Les listes de nombres montrent combien de simples, doubles, ou triples syndicats se sont produits. Les constantes dans le programme spécifient les tailles et les caractéristiques des ensembles de données.

#!/usr/bin/python
from random import sample, seed
import time
nsets,   ndelta,  ncount, setsize  = 200, 200, 5, 6
topnum, ranSeed, shoSets, shoUnion = 20, 1234, 0, 0
seed(ranSeed)
print 'Set size = {:3d},  Number max = {:3d}'.format(setsize, topnum)

for casenumber in range(ncount):
    t0 = time.time()
    sets, sizes, ssum = [], [0]*nsets, [0]*(nsets+1);
    for i in range(nsets):
        sets.append(set(sample(xrange(topnum), setsize)))

    if shoSets:
        print 'sets = {},  setSize = {},  top# = {},  seed = {}'.format(
            nsets, setsize, topnum, ranSeed)
        print 'Sets:'
        for s in sets: print s

    # Method by jwpat7
    def accrue(u, bset, csets):
        for i, c in enumerate(csets):
            y = u + [c]
            yield y
            boc = bset|c
            ts = [s for s in csets[i+1:] if boc.isdisjoint(s)]
            for v in accrue (y, boc, ts):
                yield v

    # Method by NPE
    def comb(input, lst = [], lset = set()):
        if lst:
            yield lst
        for i, el in enumerate(input):
            if lset.isdisjoint(el):
                for out in comb(input[i+1:], lst + [el], lset | set(el)):
                    yield out

    # Uncomment one of the following 2 lines to select method
    #for u in comb (sets):
    for u in accrue ([], set(), sets):
        sizes[len(u)-1] += 1
        if shoUnion: print u
    t1 = time.time()
    for t in range(nsets-1, -1, -1):
        ssum[t] = sizes[t] + ssum[t+1]
    print '{:7.3f}s  Sizes:'.format(t1-t0), [s for (s,t) in zip(sizes, ssum) if t>0]
    nsets += ndelta

Edit: en fonction accrue, les arguments (u, bset, csets) sont utilisés comme suit:

• u = liste des ensembles dans l'union actuelle des ensembles

* bset = "big set" = valeur plate de u = éléments déjà utilisé

• ECEC = ensembles de candidats = liste des ensembles pouvant être inclus

Notez que si la première ligne de accrue est remplacé par

def accrue(csets, u=[], bset=set()):

et la septième ligne par

for v in accrue (ts, y, boc):

(c'est-à-dire, si les paramètres sont ré-ordonnés et les valeurs par défaut données pour u et bset) alors accrue peut être invoqué via [accrue(listofsets)] pour produire sa liste de compatibilité des syndicats.

Concernant ValueError: zero length field name in format erreur mentionnée dans un commentaire comme se produisant lors de l'utilisation de Python 2.6, essayez les solutions suivantes.

# change:
    print "Set size = {:3d}, Number max = {:3d}".format(setsize, topnum)
# to:
    print "Set size = {0:3d}, Number max = {1:3d}".format(setsize, topnum)

des modifications similaires (ajout de numéros de champ appropriés) peuvent être nécessaires dans d'autres formats du programme. Remarque, le quoi de neuf en 2.6 page dit "Appui à la str.la méthode format () a été rétroportée à Python 2.6". Bien qu'il ne précise pas si des noms ou des numéros de champ sont requis, il ne présente pas d'exemples sans eux. En revanche, l'une ou l'autre fonctionne en 2.7.3.

2
répondu James Waldby - jwpat7 2012-11-27 14:58:26

j'utiliserais un générateur:

import itertools

def comb(seq):
   for n in range(1, len(seq)):
      for c in itertools.combinations(seq, n): # all combinations of length n
         if len(set.union(*map(set, c))) == sum(len(s) for s in c): # pairwise disjoint?
            yield list(c)

for c in comb([[1, 2, 3], [3, 6, 8], [4, 9], [6, 11]]):
   print c

cela produit:

[[1, 2, 3]]
[[3, 6, 8]]
[[4, 9]]
[[6, 11]]
[[1, 2, 3], [4, 9]]
[[1, 2, 3], [6, 11]]
[[3, 6, 8], [4, 9]]
[[4, 9], [6, 11]]
[[1, 2, 3], [4, 9], [6, 11]]

si vous devez stocker les résultats dans une seule liste:

print list(comb([[1, 2, 3], [3, 6, 8], [4, 9], [6, 11]]))
4
répondu NPE 2012-11-26 20:56:14

La suite est un circuit générateur:

def comb(input, lst = [], lset = set()):
   if lst:
      yield lst
   for i, el in enumerate(input):
      if lset.isdisjoint(el):
         for out in comb(input[i+1:], lst + [el], lset | set(el)):
            yield out

for c in comb([[1, 2, 3], [3, 6, 8], [4, 9], [6, 11]]):
   print c
2**n éléments du groupe motopropulseur).

4
répondu NPE 2012-11-26 22:04:22

en utilisant itertools.combinations, set.intersection et for-else boucle:

from itertools import *
lis=[[1, 2, 3], [3, 6, 8], [4, 9], [6, 11]]
def func(lis):
    for i in range(1,len(lis)+1):
       for x in combinations(lis,i):
          s=set(x[0])
          for y in x[1:]:
              if len(s & set(y)) != 0:
                  break
              else:
                  s.update(y)    
          else:
              yield x


for item in func(lis):
    print item

sortie:

([1, 2, 3],)
([3, 6, 8],)
([4, 9],)
([6, 11],)
([1, 2, 3], [4, 9])
([1, 2, 3], [6, 11])
([3, 6, 8], [4, 9])
([4, 9], [6, 11])
([1, 2, 3], [4, 9], [6, 11])
1
répondu Ashwini Chaudhary 2012-11-26 21:42:04

semblable à solution de NPE, mais c'est sans récursivité et elle retourne une liste:

def disjoint_combinations(seqs):
    disjoint = []
    for seq in seqs:
        disjoint.extend([(each + [seq], items.union(seq))
                            for each, items in disjoint
                                if items.isdisjoint(seq)])
        disjoint.append(([seq], set(seq)))
    return [each for each, _ in disjoint]

for each in disjoint_combinations([[1, 2, 3], [3, 6, 8], [4, 9], [6, 11]]):
    print each

Résultat:

[[1, 2, 3]]
[[3, 6, 8]]
[[1, 2, 3], [4, 9]]
[[3, 6, 8], [4, 9]]
[[4, 9]]
[[1, 2, 3], [6, 11]]
[[1, 2, 3], [4, 9], [6, 11]]
[[4, 9], [6, 11]]
[[6, 11]]
1
répondu pillmuncher 2017-05-23 12:16:44

une doublure sans utiliser le paquet itertools. Voici vos données:

lE={}
lE[0]=[1, 2, 3]
lE[1] = [3, 6, 8]
lE[2] = [4, 9]
lE[4] = [6, 11]

Voici la ligne de commande:

results=[(lE[v1],lE[v2]) for v1 in lE for v2  in lE if (set(lE[v1]).isdisjoint(set(lE[v2])) and v1>v2)]
0
répondu Max Li 2012-11-26 20:56:20