...Binomialkoeffizienten schnell berechnen?
Autor: Alexander Schimpf
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(Schnelle) Berechnung des Binomialkoeffizienten
(Fast) binomial coefficient computing
------------------------------------------------
Der folgende Algorithmus berechnet den Binomialkoeffizienten "n über k".
Ziel war es einen schnellen Algorithmus, sowie eine Datenstruktur,
die mit riesigen Zahlen umgehen kann, zu entwerfen.
Diese Unit nutzt zwei weitere Units: Funct, MyBigInt. In Funct ist die
Funktion zur Berechnung des ggTs drin. MyBigInt ist eine Klasse, die die
Darstellung von großen Zahlen ermöglicht.
Alle drei Units können auf der folgenden Seite heruntergeladen werden:
http://www.hitstec.de/archiv.php?delphi=3
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
//*** Code: CalcBinomKoeff *******************************************
unit CalcBinomKoeff;
interface
uses Sysutils, Funct, MyBigInt;
function CalculateBinomKoeff(n, k: Integer): string;
implementation
function CalculateBinomKoeff(n, k: Integer): string;
const
zaehler = 0;
nenner = 1;
Pos = 0;
var
m, i, j, iGgT: Integer;
arBruch: array [0..1] of array of Integer;
mbResult: TMyBigInt;
begin
// "n-k" soll kleiner als "k" sein, gilt wegen Symmetrie des BinomKoeff. im Nenner
if (n - k) > k then k := n - k;
m := n - k;
// Initialisierung der Variablen
for i := Low(arBruch) to High(arBruch) do SetLength(arBruch[i], m + 1);
// Setzen der gebliebenen Faktoren, "nur" n-k Faktoren sind wichtig
for i := 1 to m do
begin
arBruch[zaehler][i] := i + k;
arBruch[nenner][i] := i;
end;
arBruch[zaehler][Pos] := 1; // max{m+1: arBruch[zaehler][i]=1 für alle i=1...m}
arBruch[nenner][Pos] := 2; // max{m+1: arBruch[nenner][i]=1 für alle i=1...m}
while arBruch[nenner][Pos] <= m do
begin
for i := m downto arBruch[nenner][Pos] do
begin
for j := m downto arBruch[zaehler][Pos] do
begin
// Bestimmung des ggTs
iGgT := ggT(arBruch[nenner][i], arBruch[zaehler][j]);
if iGgT > 1 then
begin
// Kürzen
arBruch[zaehler][j] := Round(arBruch[zaehler][j] / iGgT);
arBruch[nenner][i] := Round(arBruch[nenner][i] / iGgT);
if arBruch[nenner][i] = 1 then break;
end;
end;
// Verschieben der Position im Zaehler
for j := arBruch[zaehler][Pos] to m do
if arBruch[zaehler][j] = 1 then arBruch[zaehler][Pos] := j + 1
else
break;
end;
// Verschieben der Position im Nenner
for i := arBruch[nenner][Pos] to m do
if arBruch[nenner][i] = 1 then arBruch[nenner][Pos] := i + 1
else
break;
end;
mbResult := TMyBigInt.Create(1);
try
// Faktoren im Zaehler aufmultiplizieren
for i := arBruch[zaehler][Pos] to m do mbResult.Multiply(mbResult, arBruch[zaehler][i]);
Result := mbResult.ToString;
finally
FreeAndNil(mbResult);
end;
end;
end.
//*** Code: Funct ****************************************************
unit Funct;
interface
function ggT(x, y: Integer): Integer;
implementation
function ggT(x, y: Integer): Integer;
begin
if y > x then Result := ggT(y, x)
else if y = 0 then Result := x
else
Result := ggT(y, x mod y);
end;
end.
//*** Code: MyBigInt *************************************************
unit MyBigInt;
interface
uses Sysutils, Math;
const
Base = 10;
type
TMyBigInt = class
private
Len: Integer;
Value: AnsiString;
procedure Trim;
procedure Shift(k: Integer);
procedure MultiplyAtom(Multiplier1: TMyBigInt; Multiplier2: Integer);
public
constructor Create(iValue: Integer = 0);
procedure Add(Addend1, Addend2: TMyBigInt);
procedure Multiply(Multiplier1, Multiplier2: TMyBigInt); overload;
procedure Multiply(Multiplier1: TMyBigInt; Multiplier2: Integer); overload;
function ToString: string;
procedure CopyFrom(mbCopy: TMyBigInt);
end;
implementation
constructor TMyBigInt.Create(iValue: Integer = 0);
var
sTmp: ShortString;
i: Integer;
begin
inherited Create;
sTmp := IntToStr(abs(iValue));
Len := Length(sTmp);
SetLength(Value, Len);
for i := 1 to Len do Value[i] := Chr(StrToInt(sTmp[Len - i + 1]));
end;
procedure TMyBigInt.Add(Addend1, Addend2: TMyBigInt);
{ zwei TMyBigInt miteinander addieren }
var
i, iCarry, iTemp: Integer;
begin
// Länge der Wert-Strings angleichen
iTemp := max(Addend1.Len, Addend2.Len);
SetLength(Value, iTemp);
for i := Len + 1 to iTemp do Value[i] := #0; // Für den Fall Addend1/Addend2=Self
Len := iTemp;
// Berechnung von Übertrag und Summe
iCarry := 0;
for i := 1 to Len do
begin
iTemp := iCarry;
if i <= Addend1.Len then iTemp := iTemp + Ord(Addend1.Value[i]);
if i <= Addend2.Len then iTemp := iTemp + Ord(Addend2.Value[i]);
Value[i] := Char(iTemp mod Base);
iCarry := iTemp div Base;
end;
if iCarry > 0 then
begin
Len := Len + 1;
SetLength(Value, Len);
Value[Len] := Char(iCarry);
end;
end;
procedure TMyBigInt.Multiply(Multiplier1, Multiplier2: TMyBigInt);
{ zwei TMyBigInt miteinander multipliziren }
var
mbResult, mbTemp: TMyBigInt;
i: Integer;
begin
mbResult := TMyBigInt.Create;
try
mbTemp := TMyBigInt.Create;
try
for i := 1 to Multiplier2.Len do
begin
// Multiplizieren nach der "Schulmethode"
mbTemp.MultiplyAtom(Multiplier1, Ord(Multiplier2.Value[i]));
mbTemp.Shift(i - 1);
mbResult.Add(mbResult, mbTemp);
end;
finally
FreeAndNil(mbTemp);
end;
CopyFrom(mbResult);
finally
FreeAndNil(mbResult);
end;
end;
procedure TMyBigInt.Multiply(Multiplier1: TMyBigInt; Multiplier2: Integer);
{ TMyBigInt und einen Integer multiplizieren }
var
mbTemp: TMyBigInt;
begin
mbTemp := TMyBigInt.Create(Multiplier2);
try
Multiply(Multiplier1, mbTemp);
finally
end;
end;
function TMyBigInt.ToString: string;
{ Zahl in einen String umwandeln }
var
i: Integer;
begin
Trim;
Result := '';
for i := Len downto 1 do Result := Result + IntToStr(Ord(Value[i]));
end;
procedure TMyBigInt.CopyFrom(mbCopy: TMyBigInt);
{ von mbCopy kopieren }
begin
Value := mbCopy.Value;
Len := mbCopy.Len;
end;
procedure TMyBigInt.Trim;
{ führende Nullen entfernen }
var
i, p: Integer;
begin
p := Len;
for i := Len downto 1 do
begin
if not (Value[i] in ['0']) then break;
p := i - 1;
end;
if p < Len then
begin
SetLength(Value, p);
Len := p;
end;
end;
procedure TMyBigInt.Shift(k: Integer);
{ von hinten mit k Nullen auffüllen, also mit Base^k multiplizieren }
var
i: Integer;
begin
if k = 0 then Exit;
SetLength(Value, Len + k);
for i := Len downto 1 do Value[i + k] := Value[i];
for i := 1 to k do Value[i] := #0;
Len := Len + k;
end;
procedure TMyBigInt.MultiplyAtom(Multiplier1: TMyBigInt; Multiplier2: Integer);
{ Multiplikation mit einer Ziffer }
var
i, iCarry, iTemp: Integer;
begin
// Multiplikation mit 1
if Multiplier2 = 1 then
begin
CopyFrom(Multiplier1);
Exit;
end;
SetLength(Value, Multiplier1.Len);
Len := Multiplier1.Len;
iCarry := 0;
for i := 1 to Len do
begin
iTemp := Ord(Multiplier1.Value[i]) * Multiplier2 + iCarry;
Value[i] := Char(iTemp mod Base);
iCarry := iTemp div Base;
end;
if iCarry > 0 then
begin
Len := Len + 1;
SetLength(Value, Len);
Value[Len] := Char(iCarry);
end;
end;
end.
printed from
www.swissdelphicenter.ch
developers knowledge base