Numerisk programmering i C# part 1: Løsning af ligningssystemer

Tags:    c#
<< < 123 > >>
Skrevet af Bruger #4522 @ 22.10.2007

Pivoting


I visse tilfælde vil vores A matrix være i en form som vores løsningsmetoder kan få problemer med at give en korrekt løsning på - man siger at de er ustabile. Det kan for eksempel opstå hvis element A[0,0] (det allerførste element) er nul. Systemet kan også blive ustabilt af andre årsager.

Så vores første opgave er at arrangere lidt om på A så systemet ikke bliver ustabilt. Denne "flytten-rundt" kaldes på engelsk for pivoting. Det betyder "at dreje sig", eller "at dreje om en tap", og det er altså klart hvorfor at man kaldet denne proces for netop pivoting da vi drejer lidt på matricen.

Når vi laver denne flytten-rundt er målet at befolke diagonalen i matricen med nogle "gode" elementer. Gode elementer er nogle som har en relativt set høj værdi. Så når vi laver "pivoting" går vi vores diagonalelementer igennem og bytter det ud med den række som har den største værdi (man kan også bringe kolonnerne ind i betragtningerne udover rækkerne, men det koster mere rent beregningsmæssigt uden at give en nævneværdig fordel). Vi vil kun bytte en række ud med en række der ligger under det pågældende diagonalelements egen række (ellers omgører vi bare vores tidligere ombytninger i pivoting-processen). Endvidere, for at undgår at en eventuelt skalering påvirker pivot-resultatet vil vi under pivoting betragte hvert element som et tal mellem 0 og 1 ved at skalere det i forhold til rækkens numerisk største element.

I vores skelet for klassen EquationSolver havde vi ikke noget om pivoting - vi vil derfor tilføje en privat metode, kaldet Pivoting, som tager sig at denne rykken-rundt på A. Den er her:

Fold kodeboks ind/udKode 


I vores Pivot metode gør vi brug af vores objekts private felter: matricen a, vektoren b, samt en ny tabel - kaldet swaps - som bruges til at gemme en historie over rækkebytninger som kræves i Gauss-Jordan metoden. Denne tabel er erklæret i vores klasse som de andre felter:

Fold kodeboks ind/udKode 


Denne tabel vil vi sørge for bliver initialiseret i klassens konstruktør, som derfor ser sådan ud nu:

Fold kodeboks ind/udKode 


Gauss-Jordan metdoen


Nu er vi langt om længe nået til det som det hele handler om: løsningen af ligningssystemet - men alt vores tidligere arbejde har været strengt nødvendig for overhovedet at være i stand til at finde en løsning.

I denne sektion vil vi kigge på, og implementere, Gauss-Jordan metoden.

Gauss-Jordan metoden (fremefter kaldt GJ) er en ganske god algoritme til at løse et ligningssytem; dens ulempe ligger i at den sammenlignet med de fleste andre metoder, er ganske dyr beregningsmæssigt.

Som nævnt ovenfor er vores overordnede strategi at omdanne vores A matrix til en form som er nem at arbejde med, og i GJ omdanner vi A til enhedsmatricen. En enhedsmatrix er en kvadratisk matrix med værdien 1 i diagonalen og 0 alle andre steder. F.eks. som i denne 4x4 matrix:

Fold kodeboks ind/udKode 


Vi får omdannet A til enhedsmatricen ved at subtrahere multipla af én række fra en anden. Når A er omdannet til enhedsmatricen, har vi faktisk løsningen:

Ix = A^-1b

Her er I enhedsmatricen, x vores ubekendte, A^1 den inverse af A og b er vores koefficientvektor som nu indeholder løsningen. Som det ses kan GJ altså ikke blot benyttes til at løse ligningssystemet men også til at finde As inverse matrix.

I implementationen vil As inverse blive konstrueret kolonne for kolonne mens A bliver hivet fra hindanden kolonne for kolonne, så vi skal ikke brug mere plads end blot vores to-dimensionalle a variabel. Derudover er det meget vigtigt at bruge pivoting ved GJ. Denne pivoting vil dog forkludre As inverse A^1, forstået på den måde at dets kolonner vil være i den forkerte rækkefølge. Det var derfor vi gemte rækkebytningen i Pivot-metoden i swaps tabelvariablen; med den i hånden kan vi nemlig få den rigtig A^1 frem.

Så i oversigtsform ser vores GJ metode ud som følger:


  1. Pivot vores a variabel (og b vektoren)

  2. Reducér den "drejede" a variabel til enhedsmatricen

  3. Når vi manipulerer a skal vi også huske at manipulerer b

  4. Til sidst byt rund på rækkerne i a variablen så vi får den rigtige A^1. Dette er kun nødvendig hvis vi også ønsker at finde A^1



Koden for GJ er som følger:

Fold kodeboks ind/udKode 


Gauss-elimination


I Gauss-Jordan metoden fandt vi ligningssystemets løsning ved at reducere A matricen til enhedsmatricen - hvilket betød at vi derved også fik givet As inverse matrix.

Hvis vi kun er interesseret i at løse ligningssystemet og er ligeglad med As inverse matrix, så kan vi faktisk nøjes med at reducere A til en "halv enhedsmatrix". En "halv enhedsmatrix" er selvfølgelig noget vrøvl, men betegnelsen dækker over at den nedre del (eller øvre del) af matricen er elementer med værdien 0. Følgende er et eksempel på hvad jeg mener (her har vi kun reduceret den nedre halvdel):

Fold kodeboks ind/udKode 


I ovenstående matrix angiver tallene blot elementets indeks, f.eks. betyder x22 "elementet x i matricen X på 3. række og 3. kolonne" (husk vi tæller fra nul).

I Gauss elimination er strategien netop at reducere A "halvt" så at sige - det kan ses af følgende formel hvor matricen b' angiver en modificeret b-vektor:

Fold kodeboks ind/udKode 


Når vi har fået reduceret A til en halv diagonal matrix, og har b' som i ovenstående formel, finder vi vores løsningsvektor ved at starte med den nederste række. I ovenstående formel kan vi finde løsningen for x3 direkte:

x3 = b3' / x33.

Med løsningen af x3. Kan vi nu løse x2 direkte da vi kender x3; følgende formel isoleres blot for x2:

x2 * x22 + x3 * x23 = b2'

På samme møder finder vi x1 og x0.

Gauss-elimination er en hurtigere metode end Gauss-Jordan metoden, og følgende kode viser en implementation:

Fold kodeboks ind/udKode 





<< < 123 > >>

Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (3)

User
Bruger #12679 @ 24.10.07 18:24
Yderst interessant artikel! Flere af den slags :) 5 herfra!
User
Bruger #3491 @ 02.11.07 01:20
Det er fedt med nogle mere avancerede artikler her på Udvikleren, jeg kunne dog ikke helt følge med i Crouts Faktorisering, måske pga. det sene tidspunkt.

Det er lidt uheldigt at det eksempel på et ligningssystem du angiver:
x² + y² = 1
2x+ 4y = 0
Ikke er lineært (pga. x² og y²) og derved ikke kan repræsenteres af en matrix og altså ikke løses vha. disse fremgangsmåder.

Du burde også læse om C#'s rektangulære arrays, en af C#'s fordele i forhold til Java. Der er en rigtig god illustration her http://decenturl.com/books.google/rectangular-vs-jagged og her er der en god gennemgang af fordele ulemper ved de 2 måder http://msdn.microsoft.com/msdnmag/issues/04/03/ScientificC/#S10
User
Bruger #4522 @ 02.11.07 09:40
Hej Martin,

Tak for dine kommentarer.

Jeg bemærkede slet ikke at mit eksempel på et ligningssytem ikke var lineært, hvilket ikke er særlig smart. Tak fordi du gjorde mig opmærksom på det - det vil jeg få ændret.

Og tak for linksene på C#s rektangulære arrays, dem vil jeg læse.
Du skal være logget ind for at skrive en kommentar.
t