Ordini somma sottoinsieme


22

Un insieme di nnumeri positivi ha 2^nsottoinsiemi. Chiameremo un set "bello" se nessuno di questi sottoinsiemi ha la stessa somma. {2, 4, 5, 8}è un set così bello. Poiché nessuno dei sottoinsiemi ha la stessa somma, possiamo ordinare i sottoinsiemi per somma:

[{}, {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}]

Se etichettiamo i numeri [2, 4, 5, 8]con i simboli [a, b, c, d]in ordine crescente, otteniamo il seguente ordine astratto:

[{}, {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 altro bel set di numeri positivi può avere lo stesso ordinamento astratto o uno diverso. Ad esempio, [3, 4, 8, 10]è un bel set con un diverso ordinamento astratto:

[{}, {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}]

In questa sfida, devi contare il numero di ordinamenti astratti distinti di belle serie di nnumeri positivi. Questa sequenza è OEIS A009997 e i valori noti, a partire da n=1, sono:

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

Ad esempio, per n=3, i seguenti sono i due possibili ordini astratti:

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

Per n=4, i seguenti sono i 14 possibili ordini astratti, più un bel set di esempio con quell'ordinamento:

[{}, {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]

Quanto segue non è un ordine astratto valido:

{}, {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}

Questo ordinamento implica che:

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

Sommando queste disuguaglianze si ottiene:

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

che è una contraddizione. Il tuo codice non deve contare questo ordine. Tali controesempi appaiono per la prima volta a n=5. Esempio da questo documento , esempio 2.5 a pagina 3.

Questo ordinamento non è valido nonostante il fatto che A < Bimplica che A U C < B U C, per ogni Cdisgiunto da Ae B.


Il tuo codice o programma deve essere abbastanza veloce da poterlo eseguire fino al completamento n=4prima di inviarlo.

Gli invii possono essere programmi, funzioni, ecc. Come al solito.

Le scappatoie standard sono vietate, come sempre. Questo è il golf del codice, quindi vince la risposta più breve in byte. Sentiti libero di porre domande chiarenti nei commenti.


Molto tempo non ci vediamo Isaac!
orlp

P,QPQPQpP,qQ(pq)un'Bc...

pP,qQ(pq){un',c},{B,c}

@orlp Buono per essere tornato! Penso che farò principalmente domande per il prossimo futuro
isaacg,

Potresti anche aggiungere i 14 possibili ordini per n = 4?
Peter Taylor,

Risposte:


11

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

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]))

Provalo online!

Questo ora funziona per n = 5 in circa 5 secondi. Il if~-(v in u):può essere rimosso per -18 byte ma una penalità di prestazione enorme.

Se vuoi stampare tutti gli ordini astratti così come sono stati trovati invece di contarli, aggiungili if c:print(s.x.round(),y)prima del forciclo. (I sottoinsiemi sono rappresentati da numeri interi binari in cui ogni bit corrisponde alla presenza o all'assenza di un elemento: { a , c , d } ↔ 1101₂ = 13.)

Come funziona

fconta ricorsivamente gli ordini astratti soddisfacendo un determinato elenco di vincoli. Iniziamo con i vincoli na , a + nb , b + nc , c + nd . Usando la programmazione lineare, troviamo una soluzione ai vincoli (o restituiamo 0 se non ce n'è uno); in questo caso otteniamo a = 4, b = 8, c = 12, d = 16. Arrotondiamo la soluzione a numeri interi , quindi calcola un ordinamento di riferimento ordinando tutti i suoi sottoinsiemi per la loro somma:

{ 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'arrotondamento non può causare la violazione di alcun vincolo di più di n / 2, motivo per cui abbiamo aggiunto un margine di n .

Poiché Python sortedè stabile, tutti i legami tra i sottoinsiemi vengono interrotti nello stesso ordine lessicografico inverso in cui li abbiamo generati. Quindi potremmo immaginare di sostituire { a , b , c , d } con { a · 2 ^ n + 2 ^ 0, b · 2 ^ n + 2 ^ 1, c · 2 ^ n + 2 ^ 2, d · 2 ^ n + 2 ^ 3} per ottenere lo stesso ordine senza vincoli.

Il piano è quello di classificare tutti gli altri ordinamenti astratte di un'analisi caso in base a dove prima disaccordo con l'ordine di riferimento:

O { un }> { b },
o { un } <{ b }> { c },
o { un } <{ b } <{ c }> { un , b },
o { un } <{ b } < { c } <{ a , b }> { d },

All'interno di ogni caso, aggiungiamo questi nuovi vincoli con un margine di n e chiamiamo ricorsivamente fcon i nuovi vincoli aggiunti.

Gli appunti

Per un po 'ho ipotizzato (ma non ho ipotizzato) che le soluzioni di programma lineare con margine 1 sui vincoli saranno sempre numeri interi. Questo risulta essere falso: un controesempio con n = 7 è {2.5, 30, 62.5, 73.5, 82, 87.5, 99.5}.

Python, 606 byte (più veloce, nessuna libreria esterna)

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)))

Provalo online!

Funziona per n = 5 in un quarto di secondo e n = 6 in 230 secondi (75 secondi in PyPy).

Include un solutore di programmazione lineare codificato a mano che utilizza la matematica di interi in coordinate omogenee per evitare problemi di arrotondamento in virgola mobile.



@ Mr.Xcoder Certo, grazie!
Anders Kaseorg,

@Lynn Grazie! Ho compromesso un po 'perché non voglio rallentarlo troppo — ci vogliono già quasi 3 minuti per n = 5.
Anders Kaseorg

1
@AlonAmit Sembra che ci siano voluti circa 55 minuti per n = 6. SciPy non è il migliore in LP; Ho una versione che utilizza GLPK invece di SciPy che fa n = 6 in 70 secondi. Ancora più preoccupante, la versione di SciPy ha ottenuto la risposta sbagliata (e GLPK quella giusta) ... quindi ... interessante ... mi chiedo se si tratta di SciPy # 6690 ?
Anders Kaseorg,

1
@AlonAmit # 6690 non è vero. Ma ho aggiunto options={'tol':.1}, che sembra prendersi cura del problema.
Anders Kaseorg,

0

Rubino, 308 byte, molto più veloce

Esegue il caso 4 in ~ 150 ms. Non viene utilizzata alcuna biblioteca specializzata.

->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)}}}

Si interseca in modo ricorsivo il risultato di un caso minore, per esempio

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

con i corrispondenti sottoinsiemi con un elemento aggiuntivo aggiunto, devono mantenere lo stesso ordine relativo. Assicura inoltre che il nuovo singleton sia aggiunto dopo tutti i singleton precedenti.

La parte che verifica la conformità è la stessa di prima, ma non le combinazioni da testare sono molto meno.

Versione estesa e commentata:

->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
}

Rubino, 151 byte, abbastanza lento

(il caso di tre elementi richiede << 1s, il caso di quattro è ancora in esecuzione)

->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}

Funziona sulla rappresentazione bitfield dei sottoinsiemi, quindi potrebbe essere necessario massaggiare l'output se necessario per visualizzare i sottoinsiemi stessi.

formattato:

-> 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
}

Non riesco a testarlo sopra 3 sulla mia macchina, ma questo (139 byte) dovrebbe essere funzionalmente identico alla tua soluzione. Modifiche: ...x-1=> ..x-2, .select{...}.count=> .count{...}, |(x,y)|=> |x,y|, x&~y==0||y&~x!=0=> x&~y<1||y&~x>0poiché a&~bnon posso essere negativo se non sbaglio
Asone Tuhid,

1
Guarda il n=5controesempio che ho appena aggiunto. Se non sbaglio, il tuo codice lo accetterebbe.
Isaacg,

2
Link TIO che mostra che non funziona correttamente sul controesempio: provalo online!
Isaacg,

1
La tua versione più recente sembra essere una funzione ricorsiva chiamata P, quindi non può essere anonima. Inoltre, penso che fallisca ancora a causa del controesempio che ho pubblicato.
Isaacg,

1
Per la soluzione più veloce: 280 byte Provalo online! . Si noti che è necessario includere il nome di una funzione ricorsiva ( P=). Inoltre, penso che devi restituire un numero, quindi potresti doverlo inserire .sizeda qualche parte.
Asone Tuhid,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.