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

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

Introduktion


Denne artikel er den første i hvad jeg håber bliver en lang serie af artikler der vil omhandle brugen af C# i numerisk/finansiel programmering. Artikelserien er planlagt til at omhandle emner som numerisk løsning af ligningssystemer (denne artikel), numerisk løsning af differentialligninger, implementering af Fourier transformation, prisfastsættelse af værdipapir-derivater osv. Som synliggjort af emnelisten vil denne serie hovedsageligt henvende sig til folk som er interesseret i numerisk/finansiel programmering og som ikke er bange for matematik.

Denne første artikel vil vise hvordan man kan løse ligningssystemer ved brug af C#/.NET. Noget af koden i denne artikel er løst baseret på noget Java-kode fra bogen Technical Java: Applications for Science and Engineering, skrevet af Grant Palmer. Jeg har kommenteret min kode rigtig godt, men da jeg "programmerer på engelsk", dvs. bruger engelske navne på metoder, variabler og dets lignende, er mine kommentarer også på engelsk. Jeg antager at artiklens publikum kan forstå og læse engelsk nok til at forstå mine kommentarer i koden - men selve artiklen er selvsagt på dansk.

Denne artikel er ganske lang og omfattende, hvorfor det anbefales at du sætter en kande kaffe over eller henter en Cola, alt efter smag og behag, samt evt. et lille stykke chokolade.

Før vi starter skal jeg lige sige et par ord om ligninger. Da denne artikel beskæftiger sig med implementering af nogle matematiske metoder i C# kode, vil der være en ligning her og der. Da der ikke er en nem måde at inkludere matematiske formler på så de ser pæne ud har jeg prøvet at skrive dem så godt og klart jeg kunne enten direkte i teksten eller ved bruge af en "kodeboks". I de situationer hvor formlen er for kompleks til at notere med almindelig tekst har jeg puttet en billedefil ind i artiklen som viser formlen (billedfilen har jeg opnået ved enten at skrive formlen i Word ved hjælp af Words formeleditor og så gemme det som et billede, ved at skanne formlen ind fra en bog, eller ved at finde en billedfil af formlen på Internettet). Jeg håber at det er tilfredsstillende.

Og, lad os så begynde. Vi vil starte med at kigge lidt generelt på ligningssystemer.

Ligningssystemer


I den virkelige verden kan problemer der skal løses tit beskrives med en ligning, og som oftest er der tale om et system bestående af mere end én ligning. Ligeså snart vi har to eller flere ligninger der samlet set beskriver et problem der skal løses, har vi et ligningssystem. Vi er i denne artikel interesseret i at løse, med et C# program, ligningssystemer hvor antallet af ligninger er lig med antallet af ubekendte variabler (dem som vi skal finde).

Et eksempel på et ligningssystem, taget fra ovenstående engelske wikipedia-opslag, er:



Dette ligningssystem består af to ligninger med to ubekendte (x og y) som vi ønsker at finde. Sådan et system er ganske nemt at løse, det bliver straks mere problematisk når vi har rigtig mange ligninger i vores ligningssystem, som følgende generelle formel for et ligningssystem antyder:



Ovenstående skrives typisk på matrixform:

Ax = b

hvor A er en koefficientmatrix, x en vektor med vores ønskede ubekendte drenge, og b en koefficientvektor.

Der er udviklet flere metoder til at løse ligningssystemer. Vi vil beskæftige os med, og implementere i C#, tre metoder: Gauss-Jordan metoden, Gauss-elimination og Crouts faktorisering.

Når man skal løse et ligningssystem vil man som regel ikke kunne løse det direkte (med mindre koefficientmatricen A er meget enkel), i stedet vil alle vores metoder benytte en generel strategi der går ud på at omdanne vores A matrix til en ny matrix som vi godt kan bruge til at løse systemet direkte - alt dette skal naturligvis gøres samtidig med at de matematiske love overholdes.

Hvis man er lidt rusten med hensyn til lineær algebra og har glemt nogle af dets regler, vil vores løsningsmetoder ikke give mening. Derfor er følgende liste nogle sande udsagn som vi gør brug af undervejs. Hvis du ønsker uddybning af dem må du konsultere en lærebog om lineær algebra.


  • Som ved alle ligninger gælder der også for vores ligningssystem at hvad vi gør ved venstresiden skal også gøres ved højresiden. I vores tilfælde vil det sige at hvad vi end gør ved koefficientmatricen A skal vi også gøre ved koefficientvektoren b.

  • I et ligningssystem vil løsningen ikke blive ændret såfremt jeg adderer et multipla af en hvilken som helst række til en hvilken som helst anden række i A matricen (husk at ifølge ovenstående regel må vi ikke glemme at gøre det samme ved b vektoren!).

  • Det er mig tilladt at bytte rundt på rækkerne i A matricen (husk nu den første regel: du skal så også bytte rundt på de tilsvarende elementer i b).



Ovenstående tre regler er helt essentielle i vores løsning. Heldigvis er de nemme at huske, og de virker også logiske.

Lad os nu kigge på grundplanet for vore C# klasse som skal udføre alt det hårde arbejde.

C# klassen EquationSolver


Nu skal vi kigge på lidt kode.

Vi vil i denne sektion udvikle selve skellettet af klassen EquationSolver som kan løse vores ligningssystem.

Ideen er som følger. Objektet som skal gøre alt arbejdet for os, skabes ved at kalde klassens konstruktør med tre argumenter: en to-dimensional tabel (vores koefficientmatrix A), en tabel (vores koefficientvektor b), samt en enumværdi som angiver den ønskede løsningsmetode (Gauss-Jordan metoden, Gauss-elimination, eller Crouts faktorisering). Derudover har vores klasse en åben (public) metode kaldet Solve der løser det pågældende objekts ligningssystem; dette gøres ved at Solve kalder én af tre private metoder (alt afhængig af hvilken enumværdi klienten gav som parameter ved objektkreationen) som tager sig af at finde løsningen. Tabellen b vil blive overskrevet med løsningen, endvidere vil tabellen a blive overskrevet med en omdannet matrix som vi snakkede om ovenfor.

Alt dette er tydeliggjort ved følgende kode-skellet.

Fold kodeboks ind/udKode 


Dette er blot en måde at designe en sådan klasse på. Hvis man forudser at der med tiden vil komme flere alternative løsningsmetoder til, så vil en løsning hvor hver løsningsmetode er et objekt fra en klasse der nedarver fra f.eks. en Solve-klasse, og ikke blot en metode, give en bedre fleksibilitet. I vores tilfælde er måden hvorpå man løser et ligningssystem forholdsvis statisk, hvorfor ovenstående simple design burde være fint nok.




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