Ordonnances de sous-ensemble

22

Un ensemble de nnombres positifs a des 2^nsous-ensembles. Nous appellerons un ensemble "sympa" si aucun de ces sous-ensembles n'a la même somme. {2, 4, 5, 8}est un si bel ensemble. Puisqu'aucun des sous-ensembles n'a la même somme, nous pouvons trier les sous-ensembles par somme:

[{}, {2}, {4}, {5}, {2, 4}, {2, 5}, {8}, {4, 5}, {2, 8}, {2, 4, 5}, {4, 8}, {5, 8}, {2, 4, 8}, {2, 5, 8}, {4, 5, 8}, {2, 4, 5, 8}]

Si nous étiquetons les nombres [2, 4, 5, 8]avec les symboles [a, b, c, d]dans l'ordre croissant, nous obtenons l'ordre abstrait suivant:

[{}, {a}, {b}, {c}, {a, b}, {a, c}, {d}, {b, c}, {a, d}, {a, b, c}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}]

Un autre bel ensemble de nombres positifs peut avoir le même ordre abstrait ou différent. Par exemple, [3, 4, 8, 10]est un bel ensemble avec un ordre abstrait différent:

[{}, {a}, {b}, {a, b}, {c}, {d}, {a, c}, {b, c}, {a, d}, {b, d}, {a, b, c}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}]

Dans ce défi, vous devez compter le nombre d'ordonnances abstraites distinctes de jolis ensembles de nnombres positifs. Cette séquence est OEIS A009997 , et les valeurs connues, à partir de n=1, sont:

1, 1, 2, 14, 516, 124187, 214580603

Par exemple, pour n=3, voici les deux ordres abstraits possibles:

[{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}]
[{}, {a}, {b}, {a, b}, {c}, {a, c}, {b, c}, {a, b, c}]

Pour n=4, ce qui suit sont les 14 ordonnances abstraites possibles, plus un exemple bel ensemble avec cette commande:

[{}, {a}, {b}, {a, b}, {c}, {a, c}, {b, c}, {a, b, c}, {d}, {a, d}, {b, d}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 4, 2, 1]                                       
[{}, {a}, {b}, {a, b}, {c}, {a, c}, {b, c}, {d}, {a, b, c}, {a, d}, {b, d}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 6, 3, 2]                                      
[{}, {a}, {b}, {a, b}, {c}, {a, c}, {d}, {b, c}, {a, d}, {a, b, c}, {b, d}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 7, 4, 2]                                      
[{}, {a}, {b}, {a, b}, {c}, {a, c}, {d}, {a, d}, {b, c}, {a, b, c}, {b, d}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 6, 4, 1]                                       
[{}, {a}, {b}, {a, b}, {c}, {d}, {a, c}, {b, c}, {a, d}, {b, d}, {a, b, c}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 8, 4, 3]                                      
[{}, {a}, {b}, {a, b}, {c}, {d}, {a, c}, {a, d}, {b, c}, {b, d}, {a, b, c}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 7, 4, 2]                                       
[{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}, {d}, {a, d}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 4, 3, 2]                                      
[{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {d}, {a, b, c}, {a, d}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 4, 3, 2]                                       
[{}, {a}, {b}, {c}, {a, b}, {a, c}, {d}, {b, c}, {a, d}, {a, b, c}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 5, 4, 2]                                       
[{}, {a}, {b}, {c}, {a, b}, {a, c}, {d}, {a, d}, {b, c}, {a, b, c}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 7, 6, 2]                                      
[{}, {a}, {b}, {c}, {a, b}, {d}, {a, c}, {b, c}, {a, d}, {b, d}, {a, b, c}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 6, 4, 3]                                       
[{}, {a}, {b}, {c}, {a, b}, {d}, {a, c}, {a, d}, {b, c}, {b, d}, {a, b, c}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 8, 6, 3]                                      
[{}, {a}, {b}, {c}, {d}, {a, b}, {a, c}, {b, c}, {a, d}, {b, d}, {c, d}, {a, b, c}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 6, 5, 4]                                       
[{}, {a}, {b}, {c}, {d}, {a, b}, {a, c}, {a, d}, {b, c}, {b, d}, {c, d}, {a, b, c}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [7, 6, 5, 3]

Ce qui suit n'est pas un ordre de résumé valide:

{}, {a}, {b}, {c}, {d}, {a,b}, {e}, {a,c}, {b,c}, {a,d}, {a,e}, {b,d}, {b,e}, {c,d}, {a,b,c}, {a,b,d}, {c,e}, {d,e}, {a,b,e}, {a,c,d}, {a,c,e}, {b,c,d}, {b,c,e}, {a,d,e}, {b,d,e}, {a,b,c,d}, {c,d,e}, {a,b,c,e}, {a,b,d,e}, {a,c,d,e}, {b,c,d,e}, {a,b,c,d,e}

Cette commande implique que:

d < a + b
b + c < a + d
a + e < b + d
a + b + d < c + e

La somme de ces inégalités donne:

2a + 2b + c + 2d + e < 2a + 2b + c + 2d + e

ce qui est une contradiction. Votre code ne doit pas compter cette commande. Ces contre-exemples apparaissent pour la première fois sur n=5. Exemple de cet article , exemple 2.5 à la page 3.

Cette commande est invalide malgré le fait que cela A < Bimplique que A U C < B U C, pour toute Cdisjonction de Aet B.


Votre code ou programme doit être suffisamment rapide pour que vous puissiez l'exécuter complètement n=4avant de le soumettre.

Les soumissions peuvent être des programmes, des fonctions, etc. comme d'habitude.

Les échappatoires standard sont interdites, comme toujours. C'est le golf de code, donc la réponse la plus courte en octets l'emporte. N'hésitez pas à poser des questions de clarification dans les commentaires.

isaacg
la source
Depuis longtemps, ne voyez pas isaac!
orlp
P,QPQPQpP,qQ(pq)unebc
pP,qQ(pq){une,c},{b,c}
@orlp Ravi d'être de retour! Je pense que je ferai surtout des questions dans un avenir prévisible
isaacg
Pourriez-vous également ajouter les 14 commandes possibles pour n = 4?
Peter Taylor

Réponses:

11

Python 3 + SciPy, 396 390 385 351 336 355 octets

from scipy.optimize import*
n=int(input())
r=range(n)
def f(u):
 s=linprog(r,u,[-n]*len(u),options={'tol':.1});c=s.success;y=sorted(range(c<<n),key=lambda a:s.x.round()@[a>>i&1for i in r])
 for a,b in zip(y,y[1:]):
  v=[(a>>i&1)-(b>>i&1)for i in r]
  if~-(v in u):c+=f(u+[[-z for z in v]]);u+=v,
 return+c
print(f([[(i==j-1)-(i==j)for i in r]for j in r]))

Essayez-le en ligne!

Cela fonctionne maintenant pour n = 5 en environ 5 secondes. Le if~-(v in u):peut être supprimé pour -18 octets mais une énorme pénalité de performance.

Si vous souhaitez imprimer tous les ordres abstraits tels qu'ils sont trouvés au lieu de simplement les compter, ajoutez if c:print(s.x.round(),y)avant la forboucle. (Les sous-ensembles sont représentés par des entiers binaires où chaque bit correspond à la présence ou à l'absence d'un élément: { a , c , d } ↔ 1101₂ = 13.)

Comment ça marche

fcompte récursivement les ordres abstraits satisfaisant une liste donnée de contraintes. On part des contraintes na , a + nb , b + nc , c + nd . En utilisant la programmation linéaire, nous trouvons une solution aux contraintes (ou retournons 0 s'il n'y en a pas) - dans ce cas, nous obtenons a = 4, b = 8, c = 12, d = 16. Nous arrondissons la solution aux entiers , puis calculez un ordre de référence en triant tous ses sous-ensembles par leur somme:

{ a }, { b }, { c }, { a , b }, { d }, { a , c }, { a , d }, { b , c }, { b , d }, { a , b , c }, { c , d }, { a , b , d }, { a , c , d }, { b , c , d }, {a , b , c , d }

L'arrondi ne peut pas provoquer de violation de contraintes de plus de n / 2, c'est pourquoi nous avons ajouté une marge de n .

Puisque Python sortedest stable, tous les liens entre les sous-ensembles sont rompus dans le même ordre lexicographique inverse dans lequel nous les avons générés. On pourrait donc imaginer remplacer { a , b , c , d } par { a · 2 ^ n + 2 ^ 0, b · 2 ^ n + 2 ^ 1, c · 2 ^ n + 2 ^ 2, d · 2 ^ n + 2 ^ 3} pour obtenir le même ordre sans aucun lien.

Le plan consiste à classer tous les autres ordonnances abstraites par analyse de cas en fonction de leur premier désaccord avec l'ordre de référence:

Soit { a }> { b },
soit { a } <{ b }> { c },
ou { a } <{ b } <{ c }> { a , b },
ou { a } <{ b } < { c } <{ a , b }> { d },

Dans chaque cas, nous ajoutons ces nouvelles contraintes avec une marge de n , et appelons récursivement favec les nouvelles contraintes ajoutées.

Remarques

Pendant un certain temps, j'ai supposé (mais je n'ai pas supposé) que les solutions de programme linéaire avec la marge 1 sur les contraintes seront toujours des entiers. Cela s'avère faux: un contre-exemple avec n = 7 est {2,5, 30, 62,5, 73,5, 82, 87,5, 99,5}.

Python, 606 octets (plus rapide, pas de bibliothèques externes)

n=int(input())
r=range(n)
e=enumerate
def l(u,x):
 for i,v in e(u):
  for j,a in e(v):
   if a<0:break
  else:return[0]*len(x)
  if sum(b*x[k]for k,b in e(v))>0:
   x=l([[b*w[j]-a*w[k]for k,b in e(v)if k!=j]for w in u[:i]],x[:j]+x[j+1:]);x.insert(j,0)
   for k,b in e(v):
    if k!=j:x[j]+=b*x[k];x[k]*=-a
 return x
def f(u,x):
 x=l(u,x);c=any(x);y=sorted(range(c<<n),key=lambda a:sum(x[i]*(a>>i&1)for i in r))
 for a,b in zip(y,y[1:]):
  v=[(a>>i&1)-(b>>i&1)for i in r]+[1]
  if~-(v in u):c+=f(u+[[-z for z in v[:-1]]+[1]],x);u+=v,
 return+c
print(f([[(i==j-1)-(i==j)for i in r]+[1]for j in r],[1]*(n+1)))

Essayez-le en ligne!

Cela fonctionne pour n = 5 en un quart de seconde et n = 6 en 230 secondes (75 secondes en PyPy).

Il comprend un solveur de programmation linéaire codé à la main utilisant des mathématiques entières en coordonnées homogènes pour éviter les problèmes d'arrondi à virgule flottante.

Anders Kaseorg
la source
390 octets .
M. Xcoder
@ Mr.Xcoder Bien sûr, merci!
Anders Kaseorg
@Lynn Merci! Je me suis un peu compromis parce que je ne veux pas trop le ralentir - cela prend déjà près de 3 minutes pour n = 5.
Anders Kaseorg
1
@AlonAmit On dirait qu'il a fallu environ 55 minutes pour n = 6. SciPy n'est pas le meilleur de LP; J'ai une version utilisant GLPK au lieu de SciPy qui fait n = 6 en 70 secondes. Plus inquiétant, la version SciPy a obtenu la mauvaise réponse (et GLPK la bonne)… alors euh, c'est… intéressant… Je me demande si c'est SciPy # 6690 ?
Anders Kaseorg
1
@AlonAmit # 6690 n'est-ce pas. Mais j'ai ajouté options={'tol':.1}, ce qui semble régler le problème.
Anders Kaseorg
0

Ruby, 308 octets, beaucoup plus rapide

Exécute le boîtier 4 en ~ 150 ms. Aucune bibliothèque spécialisée n'est utilisée.

->n{t=2**(n-1)
n==0 ?[[0]]:P[n-1].map{|a|b=a.map{|i|i+t}
[*0..t].repeated_combination(t).select{|m|m[0]>=a.index(n-1)}.map{|m|c,d=a.dup,b.dup;m.reverse.map{|i|c.insert(i,d.pop)};c}}.flatten(1).select{|p|p.combination(2).all?{|(x,y)|x&~y==0||y&~x!=0&&n.times.all?{|i|x!=y<<i+1}&&p.index(x&~y)<p.index(y&~x)}}}

Il récursivement entremêle le résultat d'un cas mineur, par exemple

[{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}]

avec les sous-ensembles correspondants avec un élément supplémentaire ajouté - ils doivent garder le même ordre relatif. Il garantit également que le nouveau singleton est ajouté après tous les singletons précédents.

La partie qui vérifie la conformité est la même qu'avant, mais pas les combinaisons à tester sont beaucoup moins.

Version développée et commentée:

->n{
    t=2**(n-1)
    if n==0
        [[0]]
    else
        # for each one of the previous nice orderings
        P[n-1].map { |a|
            # create the missing sets, keep order
            b = a.map{|i|i+t}
            # intersperse the two sets
            [*0..t].repeated_combination(t) # select t insertion points
                .select do |m|
                    # ensure the new singleton is after the old ones
                    m[0] >= a.index(n-1)
                end
                .map do |m|
                    # do the interspersion
                    c,d=a.dup,b.dup
                    m.reverse.map{|i|c.insert(i, d.pop)}
                    c
                end
        }.flatten(1).select{ |p|
            # check if the final ordering is still nice
            p.combination(2).all? { |(x,y)|
                (x&~y==0) || 
                (y&~x!=0) && 
                n.times.all?{|i|x!=y<<i+1} && 
                (p.index(x&~y)<p.index(y&~x))
            }
        }
    end
}

Ruby, 151 octets, assez lent

(le cas de trois éléments prend << 1s, le cas de quatre fonctionne toujours)

->n{[*1...2**n-1].permutation.select{|p|p.combination(2).all?{|(x,y)|x&~y==0||y&~x!=0&&n.times.all?{|i|x!=y<<i+1}&&p.index(x&~y)<p.index(y&~x)}}.count}

Il fonctionne sur la représentation en champ binaire des sous-ensembles, il peut donc être nécessaire de masquer la sortie si nécessaire pour afficher les sous-ensembles eux-mêmes.

formaté:

-> n {
  [*1...2**n-1]. # prepare permutations of non-empty and non-full sets
    permutation.
    select { |p|
      p.combination(2). # check all ordered pairs
        all? { |(x, y)|
          # first is subset of second 
          x &~ y == 0 ||
          # second is not subset of first
          y &~ x != 0 &&
          # first is not a right shift of second
          # (this normalizes the ordering on atoms)
          n.times.all? { |i| x != y << i+1 } &&
          # after taking out common elements, ordering agrees 
          p.index(x &~ y) < p.index(y &~ x)
        }
    }.
    count
}
réécrit
la source
Je ne peux pas le tester au-dessus de 3 sur ma machine mais cela (139 octets) devrait être fonctionnellement identique à votre solution. Changements: ...x-1=> ..x-2, .select{...}.count=> .count{...}, |(x,y)|=> |x,y|, x&~y==0||y&~x!=0=> x&~y<1||y&~x>0car a&~bne peut pas être négatif si je ne me trompe pas
Asone Tuhid
1
Regardez le n=5contre - exemple que je viens d'ajouter. Si je ne me trompe pas, votre code l'accepterait.
isaacg
2
Lien TIO montrant qu'il ne fonctionne pas correctement sur le contre-exemple: essayez-le en ligne!
isaacg
1
Votre nouvelle version semble être une fonction récursive appelée P, elle ne peut donc pas être anonyme. De plus, je pense qu'il échoue toujours en raison du contre-exemple que j'ai publié.
isaacg
1
Pour une solution plus rapide: 280 octets Essayez-le en ligne! . Notez que vous devez inclure le nom d'une fonction récursive ( P=). De plus, je pense que vous devez renvoyer un numéro, vous devrez peut-être vous y incorporer .sizequelque part.
Asone Tuhid