dynamic-programming
Multiplikation av matriskedja
Sök…
Rekursiv lösning
Multiplikation av matriskedjor är ett optimeringsproblem som kan lösas med dynamisk programmering. Med tanke på en sekvens av matriser är målet att hitta det mest effektiva sättet att multiplicera dessa matriser. Problemet är faktiskt inte att utföra multiplikationerna, utan bara att bestämma sekvensen för de involverade matrismultiplikationerna.
Låt oss säga att vi har två matriser A 1 och A 2 med dimensionen m * n och p * q . Från reglerna för matrismultiplikation vet vi att
- Vi kan multiplicera A 1 och A 2 om och bara om n = p . Att organ antalet kolumner i A 1 måste vara lika med antalet rader av A 2.
- Om det första villkoret är uppfyllt och vi multiplicerar A 1 och A 2 , får vi en ny matris, låt oss kalla det A 3 med dimensionen m * q .
- För att multiplicera A 1 och A 2 , måste vi göra några skalförökningar. Det totala antalet skalmultiplikationer som vi behöver utföra är ( m * n * q ) eller ( m * p * q ).
- A 1 * A 2 är inte lika med A 2 * A 1 .
Om vi har tre matriser A 1 , A 2 och A 3 med dimensionen m * n , n * p respektive p * q , kommer A 4 = A 1 * A 2 * A 3 att ha dimensionen m * q . Nu kan vi utföra denna matrismultiplikation på två sätt:
- Först multiplicerar vi A 1 och A 2 , sedan med resultatet multiplicerar vi A 3 . Det är: ( A 1 * A 2 ) * A 3 .
- Först vi multiplicera A 2 och A 3, sedan med det resultat som vi multiplicera A 1. Det vill säga: A 1 * ( A 2 * A 3 ).
Du kan märka att multiplikationens ordning förblir densamma, dvs. vi multiplicerar inte ( A 1 * A 3 ) * A 2 eftersom det kanske inte är giltigt. Vi ändrar endast parentes för att multiplicera en uppsättning innan vi multiplicerar den med de återstående. Hur vi placerar dessa parenteser är viktiga. Varför? Låt oss säga, dimensionen av 3 matriser A 1 , A 2 och A 3 är 10 * 100 , 100 * 5 , 5 * 50 . Sedan,
- För ( A 1 * A 2 ) * A 3 är det totala antalet skalmultiplikationer: ( 10 * 100 * 5 ) + ( 10 * 5 * 50 ) = 7500 gånger.
- För A 1 * ( A 2 * A 3 ) är det totala antalet skalmultiplikationer: ( 100 * 5 * 50 ) + ( 10 * 100 * 50 ) = 75000 gånger.
För den andra typen är antalet skalmultiplikationer 10 gånger antalet första typ! Så om du kan utforma ett sätt att ta reda på den korrekta orienteringen av parenteser som behövs för att minimera den totala skalan multiplikationen, skulle det minska både tid och minne som behövs för matrismultiplikation. Det är här matrixkedjeförstärkning är praktiskt. Här kommer vi inte att vara upptagen med den faktiska multiplikationen av matriser, vi kommer bara att ta reda på rätt parentesordning så att antalet skalmultiplikationer minimeras. Vi har matriser A 1 , A 2 , A 3 ........ A n och vi ska ta reda på det minsta antalet skalmultiplikationer som behövs för att multiplicera dessa. Vi antar att de givna dimensionerna är giltiga, dvs att det uppfyller vårt första krav på matrismultiplikation.
Vi använder divide-and-conquer-metoden för att lösa detta problem. Dynamisk programmering behövs på grund av vanliga delproblem. Till exempel: för n = 5, har vi 5 matriser A 1, A 2, A 3, A 4 och A 5. Vi vill ta reda på det minsta antalet multiplikation som krävs för att utföra denna matrismultiplikation A 1 * A 2 * A 3 * A 4 * A 5 . Låt oss koncentrera oss på ett av de många sätten: ( A 1 * A 2 ) * ( A 3 * A 4 * A 5 ).
För detta hittar vi A vänster = A 1 * A 2 . En höger = A 3 * A 4 * A 5 . Då får vi reda på ett svar = A vänster * A höger . Det totala antalet skalmultiplikationer som behövs för att ta reda på ett svar : = Det totala antalet skalmultiplikationer som behövs för att bestämma A vänster + Det totala antalet skalmultiplikationer som behövs för att bestämma A höger + Det totala antalet skalmultiplikationer som behövs för att bestämma A vänster * En rättighet .
Den sista termen, Det totala antalet skalmultiplikationer som behövs för att bestämma A vänster * En höger kan skrivas som: Antalet rader i A vänster * antalet kolumner i A vänster * antalet kolumner i A höger . (Enligt den andra regeln om matrismultiplikation)
Men vi kan också ställa in parentesen på andra sätt. Till exempel:
- 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 etc.
För varje möjliga fall bestämmer vi antalet skalförstärkningar som behövs för att ta reda på A vänster och A höger , sedan för A vänster * A höger . Om du har en allmän idé om rekursion har du redan förstått hur du utför denna uppgift. Vår algoritm kommer att vara:
- 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.
Varför är detta dynamiskt programmeringsproblem? För att bestämma ( A 1 * A 2 * A 3 ), om du redan har beräknat ( A 1 * A 2 ), kommer det att vara till hjälp i det här fallet.
För att bestämma tillståndet för denna rekursion kan vi se att för att lösa för varje fall måste vi känna till matriserna vi arbetar med. Så vi behöver ett början och slut . När vi använder klyftan och erövra kommer vårt basfall att ha mindre än 2 matriser ( börja > = slut ), där vi inte behöver multiplicera alls. Vi har två matriser: rad och kolumn . rad [i] och kolumn [i] lagrar antalet rader och kolumner för matris A i . Vi har en dp [n] [n] matris för att lagra de redan beräknade värdena och initialisera det med -1 , där -1 representerar att värdet inte har beräknats ännu. dp [i] [j] representerar antalet skalmultiplikationer som behövs för att multiplicera Ai , A i + 1 , ....., A j inklusive. Pseudokoden kommer att se ut:
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]
Komplexitet:
Värdet på början och slut kan variera från 1 till n . Det finns n 2 olika tillstånd. För varje tillstånd kör slingan in n gånger. Total tidskomplexitet: O(n³)
och minneskomplexitet: O(n²)
.