Strassens algoritme er designet til hurtig matrixmultiplikation . Den blev udviklet af Volker Strassen i 1969 og er en generalisering af Karatsubas metode til matrixmultiplikation.
I modsætning til den traditionelle matrixmultiplikationsalgoritme (ifølge formlen ), der kører i tid , multiplicerer Strassen-algoritmen matricer i tid , hvilket giver en gevinst på store tætte matricer.
På trods af at Strassens algoritme asymptotisk ikke er den hurtigste af de eksisterende hurtigmatrixmultiplikationsalgoritmer, er den nemmere at programmere og mere effektiv når man multiplicerer relativt små matricer, så det er den der oftest bruges i praksis.
Hvis vi tilføjer de samme nulrækker og kolonner til matricerne , bliver deres produkt lig med matrixen med de samme tilføjede rækker og kolonner. Derfor kan kun matricer af størrelse tages i betragtning , og andre tilfælde kan reduceres til dette ved at tilføje nuller, som kun kan fordobles.
Lad være matricer af størrelse . De kan repræsenteres som blokmatricer af størrelse fra -matricer:
Ved princippet om blokmultiplikation udtrykkes en matrix i form af deres produkt
hvor der på højre side er otte multiplikationer af matricer af størrelse . Da matricerne danner en ring , er enhver algoritme til multiplikation af -matricer, der kun bruger additioner, subtraktioner og multiplikationer, egnet til at beregne højre side. Strassen foreslog følgende algoritme med syv multiplikationer:
Hver multiplikation kan udføres rekursivt ved hjælp af den samme procedure, og addition kan udføres trivielt ved at tilføje elementer. Derefter estimeres køretiden for algoritmen gennem den rekursive relation :
Nedenfor er et eksempel på implementering af algoritmen i Python ved hjælp af NumPy- biblioteket til hurtigt at tage submatricer. Hovedfunktionen er strassen_mul. Alle matricer antages at være kvadratiske, repræsenteret ved type numpy.array, og deres størrelse er en potens af 2.
For små matrixstørrelser er direkte multiplikation hurtigere end Strassen-algoritmen på grund af det store antal tilføjelser i sidstnævnte. Grænsen for sådanne størrelser afhænger af forholdet mellem tidspunktet for addition og multiplikation af elementer og kan derfor variere afhængigt af hardwaremiljøet. I koden er konstanten ansvarlig for sit formål TRIVIAL_MULTIPLICATION_BOUND.
fra itertools import produkt import numpy som np def split_to_2x2_blocks ( matrix ): returliste ( map ( lambda row : np . hsplit ( row , 2 ), np . vsplit ( matrix , 2 ) ) ) def strassen_mul_2x2 ( lb , rb ): d = strassen_mul ( lb [ 0 ][ 0 ] + lb [ 1 ][ 1 ], rb [ 0 ][ 0 ] + rb [ 1 ][ 1 ]) d_1 = strassen_mul ( lb [ 0 ][ 1 ] - lb [ 1 ][ 1 ], rb [ 1 ][ 0 ] + rb [ 1 ][ 1 ]) d_2 = strassen_mul ( lb [ 1 ][ 0 ] - lb [ 0 ][ 0 ], rb [ 0 ][ 0 ] + rb [ 0 ][ 1 ]) venstre = strassen_mul ( lb [ 1 ][ 1 ], rb [ 1 ][ 0 ] - rb [ 0 ][ 0 ]) højre = strassen_mul ( lb [ 0 ][ 0 ], rb [ 0 ][ 1 ] - rb [ 1 ][ 1 ]) top = strassen_mul ( lb [ 0 ][ 0 ] + lb [ 0 ][ 1 ], rb [ 1 ][ 1 ]) bund = strassen_mul ( lb [ 1 ][ 0 ] + lb [ 1 ] [ 1 ], rb [ 0 ][ 0 ]) returner [[ d + d_1 + venstre - top , højre + top ], [ venstre + bund , d + d_2 + højre - bund ]] def trivial_mul ( venstre , højre ): højde , mellemstørrelse = venstre . form mid_size , højre = højre . former resultat = np . nuller (( højde , bredde )) for række , kolonne , midt i produktet ( * kort ( rækkevidde , [ højde , bredde , mellemstørrelse ])): resultat [ række ][ col ] += venstre [ række ][ midt ] * højre [ midt ][ col ] returnere resultat TRIVIAL_MULTIPLICATION_BOUND = 8 def strassen_mul ( venstre , højre ): hævde ( venstre . form == højre . form ) hævde ( venstre . form [ 0 ] == venstre . form [ 1 ]) hvis venstre . form [ 0 ] <= TRIVIAL_MULTIPLICATION_BOUND : returner trivial_mul ( venstre , højre ) hævde ( venstre form [ 0 ] % 2 == 0 ) returnere np . _ blok ( strassen_mul_2x2 ( * kort ( split_to_2x2_blocks , [ left , right ]))) )Strassen var den første til at vise muligheden for at multiplicere matricer på en mere effektiv måde end standarden. Efter udgivelsen af hans arbejde i 1969 begyndte en aktiv søgen efter en hurtigere algoritme. Den mest asymptotisk hurtige algoritme i dag er Coppersmith-Winograd-algoritmen , som giver dig mulighed for at multiplicere matricer i operationer [1] , foreslået i 1987 og forbedret i 2011 til niveauet [1] . Denne algoritme er uden praktisk interesse på grund af den astronomisk store konstant i estimering af den aritmetiske kompleksitet. Spørgsmålet om den asymptotisk begrænsende hastighed for matrixmultiplikation er ikke blevet løst. Der er Strassens formodning om, at for tilstrækkeligt store er der en algoritme til at multiplicere to matricer af størrelse i operationer, hvor et forudtildelt positivt tal er vilkårligt lille. Denne formodning er af rent teoretisk interesse, da størrelsen af matricer, som den er virkelig lille til, tilsyneladende er meget stor.
Spørgsmålet om at konstruere den hurtigste og mest stabile praktiske algoritme til at multiplicere store matricer forbliver også uløst.
Der er en modifikation af Strassen-algoritmen, der kræver 7 multiplikationer og 15 additioner (i stedet for 18 for den almindelige Strassen-algoritme).
Matricerne er opdelt i blokundermatricer som vist ovenfor.
Mellemelementer beregnes
Matrixelementer beregnes som følger:
Strassens algoritme er en bilineær algoritme, dens koefficienter er rødderne til det kubiske system af Brents ligninger . [2] For klassen af eksakte algoritmer <2x2x2> er dette et minimalt problem, hvis løsning gør det muligt at reducere antallet af multiplikationer i ringen af matrixelementer. [3] [4] Problemet med at finde nye algoritmer er, at Brent-systemet er ikke-lineært, antallet af ukendte og ligninger (disse tal falder ikke sammen) vokser hurtigt med størrelsen af matricerne, og kun løsninger med en stor antal nuller er påkrævet.
I 2013, efter delvis at overvinde disse problemer, var det muligt at finde den første praktiske bilineære algoritme til matrixmultiplikation, som er asymptotisk hurtigere end Strassen-algoritmen. [5] Smirnovs algoritme <3x3x6; 40> multiplicerer en 3X3 matrix med en 3x6 matrix ved hjælp af 40 multiplikationer i stedet for 54. Dens asymptotiske kompleksitet er . (Tensormultiplikation af algoritmen i sig selv med et cyklisk skift af argumenter fører til en algoritme for kvadratiske matricer <54x54x54; 64000> med samme kompleksitet). For en reel acceleration af multiplikation kræves betydelig optimering - fjernelse af mange duplikerede beregninger i lineære former.
I dag (2022) er dette asymptotisk den hurtigste praktiske bilineære algoritme for et vilkårligt felt af matrixelementer.
Den 5. oktober 2022 fandt DeepMind, ved hjælp af AlphaZero neurale netværksalgoritmen, flere nye algoritmer til at multiplicere matricer af forskellige dimensioner. Imidlertid er deres hastighed for et vilkårligt felt langt fra hastigheden af kendte bedste algoritmer. Så for 4X4-matricer kræver Strassen-algoritmen 49 multiplikationer, og AlphaTensor fandt en algoritme, der kræver 47 multiplikationer, men den virker kun for feltet . [6] [7]