Genera in modo efficiente tutte le partizioni vettoriali


12

Una partizione vettoriale sta dividendo un vettore in una serie di vettori in modo tale che la loro somma sia l'originale. Ecco un paio di partizioni:

[3, 1, 2] = [3, 1, 2]
[3, 1, 2] = [0, 0, 1] + [0, 0, 1] + [0, 1, 0] + [1, 0, 0] + [2, 0, 0]
[3, 1, 2] = [1, 1, 2] + [2, 0, 0]

Qui l'aggiunta vettoriale viene eseguita in base all'elemento. Una partizione valida non contiene vettori con numeri interi negativi o il vettore tutto zero.

Ora la sfida è scrivere un programma o una funzione che generi tutte le possibili partizioni vettoriali dati un vettore target. Questo può sembrare relativamente facile ...

... ma c'è una svolta. Se il vettore di input ha dimensione L e la partizione più grande che genera ha elementi M, non è possibile utilizzare più della memoria O (L * M).

Si può presumere che un numero intero utilizzi la memoria O (1). Questo significa che devi generare le partizioni mentre le generi. Inoltre, è necessario generare ogni partizione esattamente una volta. Ad esempio, queste sono la stessa partizione:

[3, 1, 2] = [3, 0, 2] + [0, 1, 0]
[3, 1, 2] = [0, 1, 0] + [3, 0, 2]

Se dovessi produrre entrambe le risposte non è valido.


Tutte le partizioni per [3, 2]:

[3, 2]
[0, 1] + [3, 1]
[0, 1] + [0, 1] + [3, 0]
[0, 1] + [0, 1] + [1, 0] + [2, 0]
[0, 1] + [0, 1] + [1, 0] + [1, 0] + [1, 0]
[0, 1] + [1, 0] + [2, 1]
[0, 1] + [1, 0] + [1, 0] + [1, 1]
[0, 1] + [1, 1] + [2, 0]
[0, 2] + [3, 0]
[0, 2] + [1, 0] + [2, 0]
[0, 2] + [1, 0] + [1, 0] + [1, 0]
[1, 0] + [2, 2]
[1, 0] + [1, 0] + [1, 2]
[1, 0] + [1, 1] + [1, 1]
[1, 1] + [2, 1]
[1, 2] + [2, 0]

Per testare la tua risposta, eseguila [3, 2, 5, 2]. Dovrebbe generare 17939 partizioni, tutte sommate a [3, 2, 5, 2], e che sono tutte uniche (è possibile verificare l'univocità ordinando prima ogni partizione lessicograficamente).


Vince il codice più breve in byte.

Risposte:


3

Python 2, 289 byte

Algoritmo di forza bruta semplice. Tratta l'intero elenco come un numero in base max(input)+1( b) e controlla ogni "numero" nell'intervallo [0, b**(L*M))per vedere se:

  1. Somme all'importo corretto
  2. È in ordine alfabetico (garantisce l'unicità)

Se l'elenco corrisponde a questi criteri, il programma lo emette con tutti i vettori tutti zero eliminati.

Utilizzo della memoria

La più grande struttura di dati che uso in questo programma è un elenco doppiamente nidificato, una lunghezza di elenco che Mcontiene la lunghezza di liss Lper dare O(L*M)memoria.

Per le mie altre strutture di dati, ho 3 interi globali O(3), 1 lunghezza elenco L( O(L)), 1 lunghezza array M( O(M)) e una copia dell'array più grande durante l'output ( O(L*M)).

In totale, questo mi dà un uso della memoria O(2*L*M + L + M + 3)che semplifica O(L*M), soddisfacendo i criteri.

Complessità temporale

Essendo un algoritmo a forza bruta, questo algoritmo è estremamente lento. Affinché il ciclo while si chiuda, deve essere l'int finale nell'array b-1. Il ciclo deve eseguire i b**(L*M)tempi prima che ciò accada.

Inoltre, ogni volta che viene eseguito l'elenco, è necessario controllare entrambe le condizioni e stampare l'elenco nel peggiore dei casi, utilizzando L*M+L+Miterazioni. Questo semplifica per dare un totale O(L*M * b**(L*M)). Ho provato a testare il mio programma [3, 2, 5, 2], ma ho rinunciato dopo 45 minuti.

Programma golf

v=input()
L=len(v)
M=sum(v)
b=max(v)
R=range
t=[L*[0]for i in R(M)]
def A(l,i):
 if i<L*M-1and~-b<l[i/L][i%L]:A(l,i+1)
 l[i/L][i%L]=-~l[i/L][i%L]%-~b
while t[-1][-1]<b:
 if v==[sum(q[i]for q in t)for i in R(L)]and all(`t[i]`>=`t[i+1]`for i in R(M-1)):print[x for x in t if[0]*L!=x]
 A(t,0)

Potrei essere in grado di giocare a golf un po 'di più, specialmente la parte incrementale. Arriva il codice non golf.


Sicuramente non l'efficienza che speravo quando ho pubblicato questa domanda, ma immagino che risolva tecnicamente il problema :)
orlp
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.