5
Tags:
.net
c#
Skrevet af
Bruger #4522
@ 24.09.2007
Introduktion
En gang i tidernes morgen havde jeg i et
Java-projekt brug for nogle matematiske funktioner udover hvad der fandtes i Javas
Math klasse; helt specifikt drejede det sig om de hyperbolske trigonometriske funktioner, Gammafunktionen og logaritemfunktionen med grundtallet 2. Det var ophavet til min Math2 klasse i Java.
Nu arbejder jeg så i .NET/C# og skulle for nylig igen bruge de omtalte matematiske funktioner, så derfor hev jeg den på daværende tidspunkt gamle Math2 klasse frem og konverterede koden til .NET (hvilket er meget nemt). .NETs
System.Math klasse havde allerede de hyperbolske trigonometriske funktioner (de er sidenhen også kommet i Javas Math klasse) så min .NET Math2 klasse indeholder kun to
statiske funktioner: nemlig
Gamma-funktionen og
logaritmefunktionen med grundtallet 2.
Denne artikel beskriver kort denne klasse. Det er ikke lang artikel, men i OOPs ånd (genbrug, genbrug og atter genbrug) skriver jeg denne artikel da andre måske kunne få nytte af det. Artiklen vil kort berøre nogle matematiske emner, men da datalogi til dels bygger på matematik burde de fleste programmører ikke have problemer med at stifte bekendtskab med lidt matematik.
Artiklen vil kort beskrive de to pågældende matematiske funktioner for at give lidt baggrund og selvfølgelig vise hvordan de er implementeret. Til sidst er gengivet hele kildekoden til .NET Math2 klassen. Det skal bemærkes, at da jeg først lavede min Math2 C# klasse, overså jeg den overlæssede
Log funktion som kan bruges til at udregne logaritmen i hvilket som helst gurndtal, den kunne jeg have anvendt i stedet for min egen Log2 funktion. For komplethedens skyld har jeg dog ladet min Log2 funktion være i Math2 klassen.
Logaritmefunktionen
De fleste af os kender logaritmefunktionen fra skolen, gymnasiet og/eller videregående uddannelse. Mange kender dog kun til logaritmefunktionen som en tast på lommeregneren, hvilet er lidt ironisk set i dets historie.
Logaritmefunktionen blev opdaget/opfundet i 1600-tallet og blev meget populær som en hurtig måde at udregne produkter på. Den tids udregninger blev jo foretaget manuelt og beregningen af store produkter var tit besværlig, men brugen af logaritme gjorde det meget nemmere. Logaritmefunktioen er den inverse funktion til eksponentialfunktionen - dvs. de udligner hindanden.
En logaritmefunktion har et grundtal. De to mest almindelige logaritmefunktioner er 10-tals logaritmen og den naturlige logaritme; de har henholdsvis grundtallet 10 og e (e er en matematisk konstant:
e = 2.71828 18284 59045 23536...).
.NETs System.Math klasse har følgende funktioner til udregning af logaritmer:
public static double Log(double d); // Grundtallet e
public static double Log10(double d); // Grundtallet 10
Siden jeg har brug for logaritmefunktionen med grundtallet 2 (Log2), skal jeg finde en måde hvorpå jeg kan udregne Log2 ud fra de allerede eksisterende funktioner. Og det er heldigvis nemt:
log_b(a) = log_z(a) / log_z(b)
Med andre ord er logaritmen af a med grundtallet b det samme som logaritmen af a med grundtallet z divideret med logaritmen af b med grundtallet z. Og jeg kan frit vælge z.
Ved at vælge z som værende den matematiske konstant e får vi for vores ønskede 2-tals logaritme:
log_2(a) = ln(a) / ln(2)
ln er den naturlige logaritme som i C# er Log funktionen. Vores C# kode for Log2 bliver derfor:
public static double Log2(double d)
{
return Math.Log(d) / Math.Log(2);
}
Gammafunktionen
Vores anden ønskede funktion er Gammafunktionen. Gammafunktionen er i familie med fakultet. Hvor den ordinære fakultet kun er gældende for ikke negative heltal, kan Gammafunktionen bruges til at udregne fakultet af både reelle og komplekse tal. Den bruges også til at løse visse typer anden-ordens differentialligninger.
Gammafunktionen er givet ved følgende integral:
Vi kan selvfølgelig ikke implementere et integral i et computerprogram, så vi skal finde en approksimation.
For at gøre an lang historie omkring numerisk analyse kort, så var det en fyr der hed
Lanczos der udledte den mest kendte approksimation (over årene er der blevet udledt flere forskellige approksimation på Gammafunktionen).
Approksimationen ser ud som følger:
Formlen ser måske lidt skræmmende ud hvis man ikke er vant til matematik, men hvis man tager hver enkelt del for sig foregår der slet ikke noget kompliceret her. Det græske symbol gamma og N er to konstanter, og vi får den mindste fejl på approksimationen hvis de antager værdierne 5 og 6.
C er en tabel af koefficienter, som jeg putter i en array i Math2 klassen. Da N = 6 og jeg også har en c[0] har jeg kun udregnet de første 7 koefficienter. Hvis man er interesseret i hvordan de udregnes se ovenstående Lanczos wikipedia side.
Vi kan nu umiddelbart konvertere ovenstående til følgende C# kode:
private static double[] c =
{
1.000000000190015,
76.18009172947146,
-86.50532032941677,
24.01409824083091,
-1.231739572450155,
0.1208650973866179,
-0.5395239384953e-5
};
public static double Gamma(double z)
{
double term1 = z + 0.5;
double term2 = z + 5.0 + 0.5;
double term3 = c[0];
int N = 6;
for(int i = 1; i <= N; ++i)
{
term3 += c[i] / (z + i);
}
return Math.Sqrt(2 * Math.PI) * Math.Pow(term2, term1) * Math.Exp(-term2) * term3 / z;
}
Den endelige kode
Vores endelige kode består af to filer. Den første, Math2.cs, er vores nye Math2 klasse med Log2 og Gamma:
using System;
using System.Collections.Generic;
using System.Text;
namespace Math2
{
class Math2
{
private static double[] c =
{
1.000000000190015,
76.18009172947146,
-86.50532032941677,
24.01409824083091,
-1.231739572450155,
0.1208650973866179,
-0.5395239384953e-5
};
public static double Log2(double d)
{
return Math.Log(d) / Math.Log(2);
}
public static double Gamma(double z)
{
double term1 = z + 0.5;
double term2 = z + 5.0 + 0.5;
double term3 = c[0];
int N = 6;
for(int i = 1; i <= N; ++i)
{
term3 += c[i] / (z + i);
}
return Math.Sqrt(2 * Math.PI) * Math.Pow(term2, term1) * Math.Exp(-term2) * term3 / z;
}
}
}
Den anden fil, Program.cs, er en såkaldt driver der tester vores nye Math2 klasse:
using System;
using System.Collections.Generic;
using System.Text;
namespace Math2
{
class Program
{
static void Main(string[] args)
{
// Let's test the Math2 class
// Test the Log2 function
// We know that:
// Log2(2) = 1;
// Log2(4) = 2;
// Log2(8) = 3;
// Log2(16) = 4;
Console.WriteLine(Math2.Log2(2));
Console.WriteLine(Math2.Log2(4));
Console.WriteLine(Math2.Log2(8));
Console.WriteLine(Math2.Log2(16));
// Test the Gamma function
// The following are some known Gamma values
//
// Gamma(-3/2) ~= 2.363
// Gamma(1/2) ~= 1.772
// Gamma(1) = 1
// Gamma(3/2) ~= 0.886
// Gamma(2) ~= 1
// Gamma(5/2) ~= 1.329
//
// So, let's see how our approximation perform:
Console.WriteLine(Math2.Gamma(-3.0/2.0));
Console.WriteLine(Math2.Gamma(0.5));
Console.WriteLine(Math2.Gamma(1));
Console.WriteLine(Math2.Gamma(3.0/2.0));
Console.WriteLine(Math2.Gamma(2));
Console.WriteLine(Math2.Gamma(5.0/2.0));
}
}
}
Hvis man kører testen ser man at vores Gammafunktion er en udmærket approksimation.
Så hvis du står og skal bruge Gamma (eller Log2) så er de her til fri benyttelse.
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 (5)
Jeg har læst artiklen og..... ja... det ligger på et noget højere niveau end jeg pt befinder mig på, selv i matematik, så jeg venter med at rate til jeg lige har forstået matematikken bag. Men ellers ser det godt ud. Desværre kan jeg ikke se hvor man lige bruger det
Fin artikel.
I Gammafunktionen ville det nok være smart at erklære c og N som en const således den bliver kompileret til konstanter, og dermed ikke behøver overleve til run-time. Tilsvarende burde der nok være et check af om z > 0 for ikke at lave noget nuldivisionsgris :-)
Hej mikl-dk,
Tak for dine fine kommentarer, som er fuldstændig rigtige.
Hej Jacob Rohde,
Selv tak. Dejligt med nogle lidt mere teoretiske artikler :-)
Ifølge MSDN så er der altså også en tredje log function i .Net
Log(double,double)
hvor den anden double er basen
Du skal være
logget ind for at skrive en kommentar.