Added binomial coefficient calculation

This commit is contained in:
Stefan Müller 2024-11-09 17:59:56 +01:00
parent 1d399cc5b6
commit be0357befd
5 changed files with 240 additions and 1 deletions

View File

@ -161,6 +161,10 @@
<Filename Value="UMultiIndexEnumerator.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="UBinomialCoefficients.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>

93
UBinomialCoefficients.pas Normal file
View File

@ -0,0 +1,93 @@
{
Solutions to the Advent Of Code.
Copyright (C) 2024 Stefan Müller
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
}
unit UBinomialCoefficients;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, Generics.Collections;
type
TCardinalArray = array of Cardinal;
TCardinalArrays = specialize TList<TCardinalArray>;
{ TBinomialCoefficientCache }
TBinomialCoefficientCache = class
private
FCache: TCardinalArrays;
procedure AddRow;
public
constructor Create;
destructor Destroy; override;
function Get(const AN, AK: Cardinal): Cardinal;
function GetCachedRowsCount: Cardinal;
end;
implementation
{ TBinomialCoefficientCache }
procedure TBinomialCoefficientCache.AddRow;
var
row: TCardinalArray;
i: Cardinal;
begin
SetLength(row, FCache.Count + 1);
row[0] := 1;
if FCache.Count > 0 then
begin
row[FCache.Count] := 1;
for i := 1 to FCache.Count - 1 do
row[i] := FCache.Last[i - 1] + FCache.Last[i];
end;
FCache.Add(row);
end;
constructor TBinomialCoefficientCache.Create;
begin
FCache := TCardinalArrays.Create;
end;
destructor TBinomialCoefficientCache.Destroy;
begin
FCache.Free;
inherited Destroy;
end;
function TBinomialCoefficientCache.Get(const AN, AK: Cardinal): Cardinal;
var
i: Cardinal;
begin
if AN < AK then
raise ERangeError.Create('Cannot calculate binomial coefficient "n choose k" with k larger than n.');
for i := FCache.Count to AN do
AddRow;
Result := FCache[AN][AK];
end;
function TBinomialCoefficientCache.GetCachedRowsCount: Cardinal;
begin
Result := FCache.Count;
end;
end.

View File

@ -152,6 +152,10 @@
<Filename Value="USnowverloadTestCases.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="UBinomialCoefficientsTestCases.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -10,7 +10,7 @@ uses
UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases,
UPulsePropagationTestCases, UStepCounterTestCases, USandSlabsTestCases, ULongWalkTestCases,
UNeverTellMeTheOddsTestCases, USnowverloadTestCases, UBigIntTestCases, UPolynomialTestCases,
UPolynomialRootsTestCases;
UPolynomialRootsTestCases, UBinomialCoefficientsTestCases;
{$R *.res}

View File

@ -0,0 +1,138 @@
{
Solutions to the Advent Of Code.
Copyright (C) 2024 Stefan Müller
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
}
unit UBinomialCoefficientsTestCases;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, fpcunit, testregistry, UBinomialCoefficients;
type
{ TBinomialCoefficientsTestCase }
TBinomialCoefficientsTestCase = class(TTestCase)
private
FBinomialCoefficientCache: TBinomialCoefficientCache;
procedure RunRangeError;
procedure AssertEqualsCalculation(const AN, AK, AExpected: Cardinal);
procedure AssertEqualsCachedRowsCount(const AExpected: Cardinal);
protected
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestZeroChooseZero;
procedure TestNChooseZero;
procedure TestNChooseN;
procedure TestNChooseK;
procedure TestCombined;
procedure TestFullRow;
procedure TestRangeError;
end;
implementation
{ TBinomialCoefficientsTestCase }
procedure TBinomialCoefficientsTestCase.RunRangeError;
begin
FBinomialCoefficientCache.Get(1, 5);
end;
procedure TBinomialCoefficientsTestCase.AssertEqualsCalculation(const AN, AK, AExpected: Cardinal);
begin
AssertEquals('Unexpected calculation result', AExpected, FBinomialCoefficientCache.Get(AN, AK));
end;
procedure TBinomialCoefficientsTestCase.AssertEqualsCachedRowsCount(const AExpected: Cardinal);
begin
AssertEquals('Unexpected cached rows count', AExpected, FBinomialCoefficientCache.GetCachedRowsCount);
end;
procedure TBinomialCoefficientsTestCase.SetUp;
begin
FBinomialCoefficientCache := TBinomialCoefficientCache.Create;
end;
procedure TBinomialCoefficientsTestCase.TearDown;
begin
FBinomialCoefficientCache.Free;
end;
procedure TBinomialCoefficientsTestCase.TestZeroChooseZero;
begin
AssertEqualsCalculation(0, 0, 1);
AssertEqualsCachedRowsCount(1);
end;
procedure TBinomialCoefficientsTestCase.TestNChooseZero;
begin
AssertEqualsCalculation(15, 0, 1);
AssertEqualsCachedRowsCount(16);
end;
procedure TBinomialCoefficientsTestCase.TestNChooseN;
begin
AssertEqualsCalculation(11, 11, 1);
AssertEqualsCachedRowsCount(12);
end;
procedure TBinomialCoefficientsTestCase.TestNChooseK;
begin
AssertEqualsCalculation(8, 3, 56);
AssertEqualsCachedRowsCount(9);
end;
procedure TBinomialCoefficientsTestCase.TestCombined;
begin
AssertEqualsCalculation(5, 1, 5);
AssertEqualsCachedRowsCount(6);
AssertEqualsCalculation(8, 4, 70);
AssertEqualsCachedRowsCount(9);
AssertEqualsCalculation(3, 1, 3);
AssertEqualsCachedRowsCount(9);
end;
procedure TBinomialCoefficientsTestCase.TestFullRow;
begin
AssertEqualsCalculation(5, 0, 1);
AssertEqualsCachedRowsCount(6);
AssertEqualsCalculation(5, 1, 5);
AssertEqualsCachedRowsCount(6);
AssertEqualsCalculation(5, 2, 10);
AssertEqualsCachedRowsCount(6);
AssertEqualsCalculation(5, 3, 10);
AssertEqualsCachedRowsCount(6);
AssertEqualsCalculation(5, 4, 5);
AssertEqualsCachedRowsCount(6);
AssertEqualsCalculation(5, 5, 1);
AssertEqualsCachedRowsCount(6);
end;
procedure TBinomialCoefficientsTestCase.TestRangeError;
begin
AssertException(ERangeError, @RunRangeError);
end;
initialization
RegisterTest('Helper', TBinomialCoefficientsTestCase);
end.