Generieren Sie effizient alle Vektorpartitionen

12

Eine Vektorpartition teilt einen Vektor in eine Reihe von Vektoren auf, sodass deren Summe das Original ist. Hier sind ein paar Partitionen:

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

Hier erfolgt die Vektoraddition elementweise. Eine gültige Partition enthält keine Vektoren mit negativen ganzen Zahlen oder den Nullvektor.

Die Herausforderung besteht nun darin, ein Programm oder eine Funktion zu schreiben, die alle möglichen Vektorpartitionen für einen Zielvektor generiert. Das mag relativ einfach klingen ...

... aber es gibt eine Wendung. Wenn der Eingabevektor die Größe L hat und die größte Partition, die er generiert, M Elemente hat, dürfen Sie nicht mehr als O (L * M) Speicher verwenden.

Sie können davon ausgehen, dass eine Ganzzahl O (1) Speicher verwendet. Dies bedeutet, dass Sie die Partitionen beim Generieren ausgeben müssen. Außerdem müssen Sie jede Partition genau einmal ausgeben. Dies sind zum Beispiel die gleichen Partitionen:

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

Wenn Sie beide ausgeben, ist Ihre Antwort ungültig.


Alle Partitionen für [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]

Um Ihre Antwort zu testen, führen Sie sie aus [3, 2, 5, 2]. Es sollte 17939 Partitionen generieren, von denen alle die Summe [3, 2, 5, 2]bilden und die alle eindeutig sind (Sie können die Eindeutigkeit testen, indem Sie zuerst jede Partition lexikografisch sortieren).


Kürzester Code in Bytes gewinnt.

orlp
quelle

Antworten:

3

Python 2, 289 Bytes

Einfacher Brute-Force-Algorithmus. Behandelt die gesamte Liste als Zahl in base max(input)+1( b) und überprüft jede "Zahl" im Bereich, [0, b**(L*M))um festzustellen, ob

  1. Beträgt den korrekten Betrag
  2. Ist in alphabetischer Reihenfolge (sorgt für Eindeutigkeit)

Wenn die Liste diesen Kriterien entspricht, gibt das Programm sie mit allen Vektoren ohne Nullen aus.

Speichernutzung

Die größte Datenstruktur, die ich in diesem Programm verwende, ist eine doppelt verschachtelte Liste, eine MListenlänge , die eine geringe Länge enthält L, um O(L*M)Speicherplatz zu schaffen.

Für meine anderen Datenstrukturen habe ich 3 globale Ints O(3), 1 Listenlänge L( O(L)), 1 Arraylänge M( O(M)) und eine Kopie des größten Arrays bei der Ausgabe ( O(L*M)).

Insgesamt ergibt sich für mich eine Speicherauslastung , die das Erfüllen der Kriterien O(2*L*M + L + M + 3)vereinfacht O(L*M).

Zeitliche Komplexität

Als Brute-Force-Algorithmus ist dieser Algorithmus extrem langsam. Damit die while-Schleife beendet wird, muss das endgültige int im Array sein b-1. Die Schleife muss b**(L*M)zuvor einige Zeit ausgeführt werden.

Außerdem muss die Liste jedes Mal, wenn sie ausgeführt wird, beide Bedingungen überprüfen und im schlimmsten Fall unter Verwendung von L*M+L+MIterationen gedruckt werden . Dies vereinfacht die Angabe eines Gesamtwertes O(L*M * b**(L*M)). Ich habe versucht, mein Programm zu testen [3, 2, 5, 2], aber nach 45 Minuten aufgegeben.

Golfprogramm

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)

Ich könnte das etwas mehr Golf spielen, besonders den Inkrement-Teil. Ungolfed Code kommt.

Blau
quelle
Auf jeden Fall nicht die Effizienz, auf die ich gehofft hatte, als ich diese Frage gepostet habe, aber ich denke, sie löst das Problem technisch :)
orlp