Suche…


Rekursive Lösung

Matrixkettenmultiplikation ist ein Optimierungsproblem, das durch dynamische Programmierung gelöst werden kann. Mit einer Folge von Matrizen ist es das Ziel, den effizientesten Weg zu finden, diese Matrizen zu multiplizieren. Das Problem besteht nicht darin, die Multiplikationen tatsächlich durchzuführen, sondern lediglich die Reihenfolge der beteiligten Matrixmultiplikationen zu bestimmen.

Nehmen wir an, wir haben zwei Matrizen A 1 und A 2 der Dimension m * n und p * q . Aus den Regeln der Matrixmultiplikation wissen wir, dass

  1. Wir können A 1 und A 2 nur dann multiplizieren, wenn n = p . Das heißt, die Anzahl der Spalten von A 1 muss der Anzahl der Zeilen von A 2 entsprechen .
  2. Wenn die erste Bedingung erfüllt ist und wir A 1 und A 2 multiplizieren, erhalten wir eine neue Matrix, nennen wir sie A 3 der Dimension m * q .
  3. Um A 1 und A 2 zu multiplizieren, müssen wir einige Skalierer-Multiplikationen durchführen. Die Gesamtzahl der Skalierer-Multiplikationen, die wir durchführen müssen, ist ( m * n * q ) oder ( m * p * q ).
  4. A 1 * A 2 ist nicht gleich A 2 * A 1 .

Wenn wir drei Matrizen A 1 , A 2 und A 3 mit der Dimension m * n , n * p und p * q haben, dann hat A 4 = A 1 * A 2 * A 3 die Dimension m * q . Jetzt können wir diese Matrixmultiplikation auf zwei Arten durchführen:

  • Zuerst multiplizieren wir A 1 und A 2 , dann multiplizieren wir A 3 mit dem Ergebnis. Das heißt: ( A 1 * A 2 ) * A 3 .
  • Zuerst multiplizieren wir A 2 und A 3 , dann multiplizieren wir A 1 mit dem Ergebnis. Das heißt: A 1 * ( A 2 * A 3 ).

Sie können feststellen, dass die Reihenfolge der Multiplikation gleich bleibt, dh ( A 1 * A 3 ) * A 2 wird nicht multipliziert, da sie möglicherweise nicht gültig ist. Wir ändern nur die Klammern , um einen Satz zu multiplizieren, bevor er mit dem Rest multipliziert wird. Wie wir diese Klammern platzieren, ist wichtig. Warum? Nehmen wir an, die Dimension der 3 Matrizen A 1 , A 2 und A 3 beträgt 10 * 100 , 100 * 5 , 5 * 50 . Dann,

  1. Für ( A 1 * A 2 ) * A 3 ist die Gesamtzahl der Skalierer-Multiplikationen: ( 10 * 100 * 5 ) + ( 10 * 5 * 50 ) = 7500 mal.
  2. Für A 1 * ( A 2 * A 3 ) beträgt die Gesamtzahl der Skalierer-Multiplikationen: ( 100 * 5 * 50 ) + ( 10 * 100 * 50 ) = 75000 mal.

Für die zweite Art der Anzahl der Scaler Multiplikation ist 10 - mal die Anzahl der ersten Art! Wenn Sie also einen Weg finden können, um die korrekte Ausrichtung der Klammern herauszufinden, die zur Minimierung der Gesamt-Skalierer-Multiplikation erforderlich ist, würde dies sowohl den Zeit- als auch den Speicherplatz für die Matrix-Multiplikation reduzieren. Hier bietet sich die Matrixkettenmultiplikation an. Hier werden wir uns nicht mit der tatsächlichen Multiplikation von Matrizen beschäftigen, wir werden nur die korrekte Klammerreihenfolge herausfinden, so dass die Anzahl der Skalierervervielfachungen minimiert wird. Wir haben die Matrizen A 1 , A 2 , A 3 ........ A n und wir ermitteln die minimale Anzahl von Scaler-Multiplikationen, die erforderlich sind, um diese zu multiplizieren. Wir gehen davon aus, dass die angegebenen Dimensionen gültig sind, dh sie erfüllen unsere erste Anforderung an die Matrixmultiplikation.

Wir werden die Divide-and-Conquer-Methode verwenden, um dieses Problem zu lösen. Dynamische Programmierung ist wegen häufiger Teilprobleme erforderlich. Zum Beispiel: für n = 5 haben wir 5 Matrizen A 1 , A 2 , A 3 , A 4 und A 5 . Wir möchten herausfinden, wie viele Multiplikationen mindestens erforderlich sind, um diese Matrixmultiplikation A 1 * A 2 * A 3 * A 4 * A 5 durchzuführen. Konzentrieren wir uns auf eine der vielen Möglichkeiten: ( A 1 * A 2 ) * ( A 3 * A 4 * A 5 ).

Für diesen finden wir A left = A 1 * A 2 . A rechts = A 3 * A 4 * A 5 . Dann erfahren wir eine Antwort = A left * A right . Die Gesamtzahl der zur Ermittlung von Skalierern erforderlichen Skalierern A Antwort : = Die Gesamtzahl der zur Bestimmung von A left erforderlichen Skaliermultiplikationen + Die Gesamtzahl der zur Bestimmung von A right erforderlichen Skaliermultiplikationen + Die Gesamtzahl der zur Bestimmung von A left benötigten Skaliervervielfachungen * Ein recht

Der letzte Ausdruck, Die Gesamtzahl der Skalierer-Multiplikationen, die erforderlich sind, um A left * A right zu bestimmen: Die Anzahl der Zeilen in A left * die Anzahl der Spalten in A left * die Anzahl der Spalten in A right . (Nach der zweiten Regel der Matrixmultiplikation)

Aber wir könnten die Klammern auch auf andere Weise setzen. Zum Beispiel:

  • A 1 * ( A 2 * A 3 * A 4 * A 5 )
  • ( A 1 * A 2 * A 3 ) * ( A 4 * A 5 )
  • ( A 1 * A 2 * A 3 * A 4 ) * A 5 usw.

Für jeden möglichen Fall bestimmen wir die Anzahl der Scaler-Multiplikationen, die erforderlich sind, um A left und A right herauszufinden, dann für A left * A right . Wenn Sie eine allgemeine Vorstellung von Rekursion haben, haben Sie bereits verstanden, wie Sie diese Aufgabe ausführen. Unser Algorithmus wird sein:

- Set parenthesis in all possible ways.
- Recursively solve for the smaller parts.
- Find out the total number of scaler multiplication by merging left and right.
- Of all possible ways, choose the best one.

Warum ist das ein dynamisches Programmierproblem? Wenn Sie ( A 1 * A 2 * A 3 ) bereits berechnet haben ( A 1 * A 2 ), ist dies in diesem Fall hilfreich.

Um den Status dieser Rekursion zu bestimmen, können wir sehen, dass wir für jeden Fall die Bandbreite der Matrizen kennen müssen, mit denen wir arbeiten. Also brauchen wir einen Anfang und ein Ende . Bei der Verwendung von "Teilen" und "Erobern" werden in unserem Basisfall weniger als 2 Matrizen ( Anfang > = Ende ) vorhanden sein, bei denen wir uns gar nicht vermehren müssen. Wir haben 2 Arrays: Zeile und Spalte . Zeile [i] und Spalte [i] speichern die Anzahl der Zeilen und Spalten für Matrix A i . Wir haben ein dp [n] [n] -Array, um die bereits berechneten Werte zu speichern und mit -1 zu initialisieren, wobei -1 für den noch nicht berechneten Wert steht. dp [i] [j] steht für die Anzahl der Skalierer-Multiplikationen, die zum Multiplizieren von A i , A i + 1 , ..., A j einschließlich erforderlich sind. Der Pseudo-Code sieht folgendermaßen aus:

Procedure matrixChain(begin, end):
if begin >= end
    Return 0
else if dp[begin][end] is not equal to -1
    Return dp[begin][end]
end if
answer := infinity
for mid from begin to end
    operation_for_left := matrixChain(begin, mid)
    operation_for_right := matrixChain(mid+1, right)
    operation_for_left_and_right := row[begin] * column[mid] * column[end]
    total := operation_for_left + operation_for_right + operation_for_left_and_right
    answer := min(total, answer)
end for
dp[begin][end] := answer
Return dp[begin][end]

Komplexität:

Der Wert für Anfang und Ende kann zwischen 1 und n liegen . Es gibt n 2 verschiedene Zustände. Für jeden Zustand wird die Schleife n mal ausgeführt. Gesamtzeitkomplexität: O(n³) und Speicherkomplexität: O(n²) .



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow