Added bisection variant for integers instead of intervals

This commit is contained in:
Stefan Müller 2024-05-26 18:59:47 +02:00
parent 7db8f948c5
commit 5f93ad7869
3 changed files with 83 additions and 10 deletions

View File

@ -91,6 +91,8 @@ type
class function FromBinaryString(const AValue: string): TBigInt; static; class function FromBinaryString(const AValue: string): TBigInt; static;
end; end;
TBigIntArray = array of TBigInt;
{ Operators } { Operators }
operator := (const A: Int64): TBigInt; operator := (const A: Int64): TBigInt;

View File

@ -52,8 +52,11 @@ type
// Returns root-isolating intervals for non-negative, non-multiple roots. // Returns root-isolating intervals for non-negative, non-multiple roots.
class function BisectIsolation(constref APolynomial: TBigIntPolynomial): TIsolatingIntervalArray; class function BisectIsolation(constref APolynomial: TBigIntPolynomial): TIsolatingIntervalArray;
// Returns root-isolating intervals for non-multiple roots in the interval [0, 2^boundexp]. // Returns root-isolating intervals for non-multiple roots in the interval [0, 2^boundexp].
class function BisectIsolation(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal): class function BisectIsolation(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal;
TIsolatingIntervalArray; const AFindIntegers: Boolean = False): TIsolatingIntervalArray;
// Returns non-negative, non-multiple, integer roots in the interval [0, 2^boundexp].
class function BisectInteger(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal):
TBigIntArray;
end; end;
implementation implementation
@ -112,8 +115,8 @@ end;
// This is adapted from // This is adapted from
// https://en.wikipedia.org/wiki/Real-root_isolation#Bisection_method // https://en.wikipedia.org/wiki/Real-root_isolation#Bisection_method
class function TPolynomialRoots.BisectIsolation(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal): class function TPolynomialRoots.BisectIsolation(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal;
TIsolatingIntervalArray; const AFindIntegers: Boolean): TIsolatingIntervalArray;
type type
TWorkItem = record TWorkItem = record
C, K: Cardinal; C, K: Cardinal;
@ -149,12 +152,11 @@ begin
varq := item.P.RevertOrderOfCoefficients.TranslateVariableByOne; varq := item.P.RevertOrderOfCoefficients.TranslateVariableByOne;
v := varq.CalcSignVariations; v := varq.CalcSignVariations;
if v = 1 then //WriteLn;
begin //WriteLn('var((x+1)^n*q(1/(x+1))): ', v);
// Found isolating interval.
iso.Add(CreateIsolatingInterval(item.C, item.K, 1, ABoundExp)); if (v > 1)
end or ((v = 1) and AFindIntegers and (item.K < ABoundExp)) then
else if v > 1 then
begin begin
// Bisects, first new work item is (2c, k + 1, 2^n * q(x/2)). // Bisects, first new work item is (2c, k + 1, 2^n * q(x/2)).
item.C := item.C << 1; item.C := item.C << 1;
@ -165,6 +167,12 @@ begin
item.C := item.C + 1; item.C := item.C + 1;
item.P := item.P.TranslateVariableByOne; item.P := item.P.TranslateVariableByOne;
stack.Push(item); stack.Push(item);
end
else if v = 1 then
begin
// Found isolating interval.
//WriteLn('Found isolating interval (' + IntToStr(item.C) + ', ' + IntToStr(item.K) + ', 1).');
iso.Add(CreateIsolatingInterval(item.C, item.K, 1, ABoundExp));
end; end;
end; end;
Result := iso.ToArray; Result := iso.ToArray;
@ -172,5 +180,29 @@ begin
stack.Free; stack.Free;
end; end;
class function TPolynomialRoots.BisectInteger(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal):
TBigIntArray;
var
intervals: TIsolatingIntervalArray;
i: TIsolatingInterval;
r: specialize TList<TBigInt>;
value: Int64;
begin
// Calculates isolating intervals.
intervals := BisectIsolation(APolynomial, ABoundExp, True);
r := specialize TList<TBigInt>.Create;
for i in intervals do
if i.H = 0 then
r.Add(i.A)
else if i.A.TryToInt64(value) and (APolynomial.CalcValueAt(value) = 0) then
r.Add(value)
else if i.B.TryToInt64(value) and (APolynomial.CalcValueAt(value) = 0) then
r.Add(value);
Result := r.ToArray;
r.Free;
end;
end. end.

View File

@ -32,9 +32,11 @@ type
private private
procedure AssertBisectIntervals(AIsolatingIntervals: TIsolatingIntervalArray; constref AExpectedRoots: procedure AssertBisectIntervals(AIsolatingIntervals: TIsolatingIntervalArray; constref AExpectedRoots:
array of Cardinal); array of Cardinal);
procedure AssertBisectIntegers(ARoots: TBigIntArray; constref AExpectedRoots: array of Cardinal);
published published
procedure TestBisectNoBound; procedure TestBisectNoBound;
procedure TestBisectWithBound; procedure TestBisectWithBound;
procedure TestBisectInteger;
end; end;
implementation implementation
@ -64,6 +66,29 @@ begin
end; end;
end; end;
procedure TPolynomialRootsTestCase.AssertBisectIntegers(ARoots: TBigIntArray; constref AExpectedRoots:
array of Cardinal);
var
exp: Cardinal;
found: Boolean;
i, foundIndex: Integer;
begin
AssertEquals('Unexpected number of integer roots.', Length(AExpectedRoots), Length(ARoots));
for exp in AExpectedRoots do
begin
found := False;
for i := 0 to Length(ARoots) - 1 do
if ARoots[i] = exp then
begin
found := True;
foundIndex := i;
Break;
end;
AssertTrue('Expected root ' + IntToStr(exp) + ' not found.', found);
Delete(ARoots, foundIndex, 1);
end;
end;
procedure TPolynomialRootsTestCase.TestBisectNoBound; procedure TPolynomialRootsTestCase.TestBisectNoBound;
const const
expRoots: array of Cardinal = (34000, 23017, 5); expRoots: array of Cardinal = (34000, 23017, 5);
@ -92,6 +117,20 @@ begin
AssertBisectIntervals(r, expRoots); AssertBisectIntervals(r, expRoots);
end; end;
procedure TPolynomialRootsTestCase.TestBisectInteger;
const
expRoots: array of Cardinal = (23017, 5);
var
a: TBigIntPolynomial;
r: TBigIntArray;
begin
// y = 3 * (x - 34000) * (x - 23017) * (x - 5) * (x^2 - 19) * (x + 112)
// = 3 * x^6 - 170730 * x^5 + 2329429920 * x^4 + 251300082690 * x^3 - 1270471872603 * x^2 + 4774763204640 * x - 24979889760000
a := TBigIntPolynomial.Create([-24979889760000, 4774763204640, -1270471872603, 251300082690, 2329429920, -170730, 3]);
r := TPolynomialRoots.BisectInteger(a, 15);
AssertBisectIntegers(r, expRoots);
end;
initialization initialization
RegisterTest(TPolynomialRootsTestCase); RegisterTest(TPolynomialRootsTestCase);