Updated day 12 WIP performance refactor

- Added combinations calculation directly to the multi-index enumerable
  (TValidationPositionOffsets) and added new intermediate derived class
  TAccumulatedCombinationsMultiIndexStrategy to make this reusable for the other
  enumerables
- Removed TBinomialCoefficientCache instances and used new global instance instead
- Renamed TValidationToDamageAssignments to TDamageToValidationAssignments
This commit is contained in:
Stefan Müller 2024-11-17 23:54:08 +01:00
parent 3f7fb4a548
commit ec6928679a
2 changed files with 131 additions and 100 deletions

View File

@ -41,6 +41,7 @@ const
type type
TIntegerList = specialize TList<Integer>; TIntegerList = specialize TList<Integer>;
TInt64Array = array of Int64;
implementation implementation

View File

@ -63,9 +63,9 @@ type
end; end;
TBlocks = specialize TObjectList<TBlock>; TBlocks = specialize TObjectList<TBlock>;
{ TValidationToDamageAssignments } { TDamageToValidationAssignments }
TValidationToDamageAssignments = class(TEnumerableMultiIndexStrategy) TDamageToValidationAssignments = class(TEnumerableMultiIndexStrategy)
private private
FValidation: TIntegerList; FValidation: TIntegerList;
FValidationLengths: TValidationLengths; FValidationLengths: TValidationLengths;
@ -92,14 +92,34 @@ type
TValidationPositionInfos = specialize TList<TValidationPositionInfo>; TValidationPositionInfos = specialize TList<TValidationPositionInfo>;
TConditionRecord = class;
{ TAccumulatedCombinationsMultiIndexStrategy }
TAccumulatedCombinationsMultiIndexStrategy = class(TEnumerableMultiIndexStrategy)
private
FAccumulatedCombinations: TInt64Array;
protected
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; virtual;
abstract;
function UpdateCombinations(const AValidationResult: TIndexValidationResult; constref ACurrentIndexArray:
TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult;
public
function GetCombinations: Int64;
end;
{ TValidationPositionOffsets } { TValidationPositionOffsets }
TValidationPositionOffsets = class(TEnumerableMultiIndexStrategy) TValidationPositionOffsets = class(TAccumulatedCombinationsMultiIndexStrategy)
private private
FValidation: TIntegerList; FConditionRecord: TConditionRecord;
FPositionInfos: TValidationPositionInfos; FPositionInfos: TValidationPositionInfos;
FBlockLength, FStartIndex, FStopIndex: Integer;
protected
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; override;
public public
constructor Create(constref AValidation: TIntegerList; constref APositionInfos: TValidationPositionInfos); constructor Create(constref AConditionRecord: TConditionRecord; constref APositionInfos: TValidationPositionInfos;
const ABlockLength, AStartIndex, AStopIndex: Integer);
function GetCardinality: Integer; override; function GetCardinality: Integer; override;
function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer; function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer;
out AStartIndexValue: Integer): Boolean; override; out AStartIndexValue: Integer): Boolean; override;
@ -111,7 +131,6 @@ type
TConditionRecord = class TConditionRecord = class
private private
FBinomialCoefficients: TBinomialCoefficientCache;
FValidation: TIntegerList; FValidation: TIntegerList;
// List of non-empty, maximum-length parts of the pattern without operational springs ("blocks"). // List of non-empty, maximum-length parts of the pattern without operational springs ("blocks").
FBlocks: TBlocks; FBlocks: TBlocks;
@ -129,17 +148,13 @@ type
function CalcCombinationsBlockSingleValidation(constref ABlock: TBlock; const AIndex: Integer): Int64; function CalcCombinationsBlockSingleValidation(constref ABlock: TBlock; const AIndex: Integer): Int64;
function CalcCombinationsBlockMultiValidations(constref ABlock: TBlock; constref AIndices: TIndexArray; function CalcCombinationsBlockMultiValidations(constref ABlock: TBlock; constref AIndices: TIndexArray;
const AStartIndex, AStopIndex: Integer): Int64; const AStartIndex, AStopIndex: Integer): Int64;
function CalcCombinationsBlockAssignedValidations(const ABlockLength: Integer; constref APositionInfos:
TValidationPositionInfos; constref AOffsets: TIndexArray; const AStartIndex, AStopIndex: Integer): Int64;
function CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer): Int64;
public public
constructor Create(constref ABinomialCoefficients: TBinomialCoefficientCache); constructor Create;
destructor Destroy; override; destructor Destroy; override;
// Adds all non-empty, maximum-length parts of the pattern without operational springs ("blocks"). // Adds all non-empty, maximum-length parts of the pattern without operational springs ("blocks").
procedure AddBlocks(const APattern: string); procedure AddBlocks(const APattern: string);
function GenerateBlockAssignments: Int64; function GenerateBlockAssignments: Int64;
// TODO: Blocks is not needed? function CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer): Int64;
//property Blocks: TBlocks read FBlocks;
property Validation: TIntegerList read FValidation; property Validation: TIntegerList read FValidation;
end; end;
@ -147,8 +162,6 @@ type
THotSprings = class(TSolver) THotSprings = class(TSolver)
private private
// Keeping the binomial coefficients calculator here so it can be shared for all lines.
FBinomialCoefficients: TBinomialCoefficientCache;
// TODO: Remove FDebugIndex. // TODO: Remove FDebugIndex.
FDebugIndex: Integer; FDebugIndex: Integer;
public public
@ -206,9 +219,9 @@ begin
inherited Destroy; inherited Destroy;
end; end;
{ TValidationToDamageAssignments } { TDamageToValidationAssignments }
function TValidationToDamageAssignments.CalcValidationSpan(constref ACurrentIndexArray: TIndexArray; function TDamageToValidationAssignments.CalcValidationSpan(constref ACurrentIndexArray: TIndexArray;
const ALastDamageIndex, AValidationNumber: Integer): Integer; const ALastDamageIndex, AValidationNumber: Integer): Integer;
var var
spanStart: Integer; spanStart: Integer;
@ -221,7 +234,7 @@ begin
Inc(Result, FDamages[ALastDamageIndex].Start - FDamages[spanStart].Start); Inc(Result, FDamages[ALastDamageIndex].Start - FDamages[spanStart].Start);
end; end;
constructor TValidationToDamageAssignments.Create(constref AValidation: TIntegerList; constref AValidationLengths: constructor TDamageToValidationAssignments.Create(constref AValidation: TIntegerList; constref AValidationLengths:
TValidationLengths; constref ADamages: TDamages; const AStartIndex, AStopIndex: Integer); TValidationLengths; constref ADamages: TDamages; const AStartIndex, AStopIndex: Integer);
begin begin
FValidation := AValidation; FValidation := AValidation;
@ -231,12 +244,12 @@ begin
FValidationStopIndex := AStopIndex; FValidationStopIndex := AStopIndex;
end; end;
function TValidationToDamageAssignments.GetCardinality: Integer; function TDamageToValidationAssignments.GetCardinality: Integer;
begin begin
Result := FDamages.Count; Result := FDamages.Count;
end; end;
function TValidationToDamageAssignments.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; function TDamageToValidationAssignments.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray;
const ACurrentIndex: Integer; out AStartIndexValue: Integer): Boolean; const ACurrentIndex: Integer; out AStartIndexValue: Integer): Boolean;
begin begin
Result := True; Result := True;
@ -246,7 +259,7 @@ begin
AStartIndexValue := FValidationStartIndex; AStartIndexValue := FValidationStartIndex;
end; end;
function TValidationToDamageAssignments.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; function TDamageToValidationAssignments.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray;
const ACurrentIndex: Integer): TIndexValidationResult; const ACurrentIndex: Integer): TIndexValidationResult;
var var
i, prev, firstSkip: Integer; i, prev, firstSkip: Integer;
@ -296,13 +309,71 @@ begin
Result := ivrValid; Result := ivrValid;
end; end;
{ TAccumulatedCombinationsMultiIndexStrategy }
function TAccumulatedCombinationsMultiIndexStrategy.UpdateCombinations(const AValidationResult: TIndexValidationResult;
constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult;
var
combinations: Int64;
begin
Result := AValidationResult;
if Result = ivrValid then
begin
combinations := CalcCombinations(ACurrentIndexArray, ACurrentIndex);
if combinations = 0 then
Result := ivrBacktrack
else if ACurrentIndex > 0 then
FAccumulatedCombinations[ACurrentIndex] := combinations * FAccumulatedCombinations[ACurrentIndex - 1]
else begin
SetLength(FAccumulatedCombinations, GetCardinality);
FAccumulatedCombinations[ACurrentIndex] := combinations;
end;
end;
end;
function TAccumulatedCombinationsMultiIndexStrategy.GetCombinations: Int64;
begin
Result := FAccumulatedCombinations[GetCardinality - 1];
end;
{ TValidationPositionOffsets } { TValidationPositionOffsets }
constructor TValidationPositionOffsets.Create(constref AValidation: TIntegerList; constref APositionInfos: function TValidationPositionOffsets.CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex:
TValidationPositionInfos); Integer): Int64;
var
space, start, stop: Integer;
begin begin
FValidation := AValidation; stop := FPositionInfos[ACurrentIndex].ValidationIndex - 1;
if ACurrentIndex > 0 then
begin
space := ACurrentIndexArray[ACurrentIndex] - ACurrentIndexArray[ACurrentIndex - 1]
- FConditionRecord.Validation[FPositionInfos[ACurrentIndex - 1].ValidationIndex] - 2;
start := FPositionInfos[ACurrentIndex - 1].ValidationIndex + 1;
Result := FConditionRecord.CalcCombinationsWildcardSequence(space, start, stop);
end
else begin
// Handles first calculated offset.
space := ACurrentIndexArray[0] - 2;
Result := FConditionRecord.CalcCombinationsWildcardSequence(space, FStartIndex, stop);
end;
if (Result > 0) and (ACurrentIndex + 1 = GetCardinality) then
begin
// Handles last calculated offset.
space := FBlockLength - ACurrentIndexArray[ACurrentIndex] - FConditionRecord.Validation[FPositionInfos.Last.ValidationIndex];
Result := Result * FConditionRecord.CalcCombinationsWildcardSequence(space, FPositionInfos.Last.ValidationIndex + 1, FStopIndex);
end;
end;
constructor TValidationPositionOffsets.Create(constref AConditionRecord: TConditionRecord; constref APositionInfos:
TValidationPositionInfos; const ABlockLength, AStartIndex, AStopIndex: Integer);
begin
FConditionRecord := AConditionRecord;
FPositionInfos := APositionInfos; FPositionInfos := APositionInfos;
FBlockLength := ABlockLength;
FStartIndex := AStartIndex;
FStopIndex := AStopIndex;
inherited Create;
end; end;
function TValidationPositionOffsets.GetCardinality: Integer; function TValidationPositionOffsets.GetCardinality: Integer;
@ -317,13 +388,12 @@ var
begin begin
info := FPositionInfos[ACurrentIndex]; info := FPositionInfos[ACurrentIndex];
// Calculates start value such that the validation number just includes MinEnd. // Calculates start value such that the validation number just includes MinEnd.
//AStartIndexValue := info.MinEnd - FValidation[info.ValidationIndex] + 1;
AStartIndexValue := info.MinStart; AStartIndexValue := info.MinStart;
// Adjusts start value to avoid overlap of this validation number with the previous one (the one from previous // Adjusts start value to avoid overlap of this validation number with the previous one (the one from previous
// position info). // position info).
if ACurrentIndex > 0 then if ACurrentIndex > 0 then
AStartIndexValue := Max(AStartIndexValue, AStartIndexValue := Max(AStartIndexValue,
ACurrentIndexArray[ACurrentIndex - 1] + FValidation[FPositionInfos[ACurrentIndex - 1].ValidationIndex] + 1); ACurrentIndexArray[ACurrentIndex - 1] + FConditionRecord.Validation[FPositionInfos[ACurrentIndex - 1].ValidationIndex] + 1);
Result := True; Result := True;
end; end;
@ -334,6 +404,8 @@ begin
Result := ivrValid Result := ivrValid
else else
Result := ivrBacktrack; Result := ivrBacktrack;
Result := UpdateCombinations(Result, ACurrentIndexArray, ACurrentIndex);
end; end;
{ TConditionRecord } { TConditionRecord }
@ -413,7 +485,7 @@ function TConditionRecord.CalcCombinationsBlock(constref ABlock: TBlock; const A
var var
i, j, k: Integer; i, j, k: Integer;
indices: TIndexArray; indices: TIndexArray;
validationToDamageAssignments: TValidationToDamageAssignments; validationToDamageAssignments: TDamageToValidationAssignments;
begin begin
{$ifdef debug} {$ifdef debug}
Write(' ', ABlock.Pattern, ' '); Write(' ', ABlock.Pattern, ' ');
@ -464,7 +536,7 @@ begin
Result := 0; Result := 0;
// Assigns validation numbers to specific damages. // Assigns validation numbers to specific damages.
validationToDamageAssignments := TValidationToDamageAssignments.Create(FValidation, FValidationLengths, ABlock.Damages, validationToDamageAssignments := TDamageToValidationAssignments.Create(FValidation, FValidationLengths, ABlock.Damages,
AStartIndex, AStopIndex); AStartIndex, AStopIndex);
{$ifdef debug} {$ifdef debug}
WriteLn(' validation numbers (indices) per damages:'); WriteLn(' validation numbers (indices) per damages:');
@ -551,82 +623,17 @@ begin
WriteLn(' offsets'); WriteLn(' offsets');
{$endif} {$endif}
Result := 0; Result := 0;
validationPositionOffsets := TValidationPositionOffsets.Create(FValidation, positions); validationPositionOffsets := TValidationPositionOffsets.Create(Self, positions, Length(ABlock.Pattern),
AStartIndex, AStopIndex);
for offsets in validationPositionOffsets do for offsets in validationPositionOffsets do
Result := Result Result := Result + validationPositionOffsets.GetCombinations;
+ CalcCombinationsBlockAssignedValidations(Length(ABlock.Pattern), positions, offsets, AStartIndex, AStopIndex);
validationPositionOffsets.Free; validationPositionOffsets.Free;
positions.Free; positions.Free;
end; end;
function TConditionRecord.CalcCombinationsBlockAssignedValidations(const ABlockLength: Integer; constref APositionInfos: constructor TConditionRecord.Create;
TValidationPositionInfos; constref AOffsets: TIndexArray; const AStartIndex, AStopIndex: Integer): Int64;
var
i, space: Integer;
begin begin
{$ifdef debug}
Write(' ');
for i in AOffsets do
Write(i, ' ');
Write(' count/space/freedoms: ');
{$endif}
space := AOffsets[0] - 2;
Result := CalcCombinationsWildcardSequence(space, AStartIndex, APositionInfos[0].ValidationIndex - 1);
if Result = 0 then begin
{$ifdef debug}
WriteLn(' result: ', Result);
{$endif}
Exit;
end;
for i := 0 to APositionInfos.Count - 2 do begin
space := AOffsets[i + 1] - AOffsets[i] - FValidation[APositionInfos[i].ValidationIndex] - 2;
Result := Result * CalcCombinationsWildcardSequence(space, APositionInfos[i].ValidationIndex + 1, APositionInfos[i + 1].ValidationIndex - 1);
if Result = 0 then begin
{$ifdef debug}
WriteLn(' result: ', Result);
{$endif}
Exit;
end;
end;
space := ABlockLength - AOffsets[APositionInfos.Count - 1] - FValidation[APositionInfos.Last.ValidationIndex];
Result := Result * CalcCombinationsWildcardSequence(space, APositionInfos.Last.ValidationIndex + 1, AStopIndex);
{$ifdef debug}
WriteLn(' result: ', Result);
{$endif}
end;
function TConditionRecord.CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer):
Int64;
var
count, freedoms: Integer;
begin
if AStartIndex < AStopIndex + 1 then
begin
count := AStopIndex + 1 - AStartIndex;
freedoms := ASequenceLength - FValidationLengths[AStartIndex, AStopIndex + 1];
{$ifdef debug}
Write(count, '/', ASequenceLength, '/', freedoms, ' ');
{$endif}
if freedoms >= 0 then
Result := FBinomialCoefficients.Get(count + freedoms, freedoms)
else
Result := 0;
end
else begin
Result := 1;
{$ifdef debug}
Write('X ');
{$endif}
end;
end;
constructor TConditionRecord.Create(constref ABinomialCoefficients: TBinomialCoefficientCache);
begin
FBinomialCoefficients := ABinomialCoefficients;
FBlocks := TBlocks.Create; FBlocks := TBlocks.Create;
FValidation := TIntegerList.Create; FValidation := TIntegerList.Create;
end; end;
@ -710,17 +717,40 @@ begin
WriteLn(' missed: ', misses, '/', count); WriteLn(' missed: ', misses, '/', count);
end; end;
function TConditionRecord.CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer):
Int64;
var
count, freedoms: Integer;
begin
if AStartIndex < AStopIndex + 1 then
begin
count := AStopIndex + 1 - AStartIndex;
freedoms := ASequenceLength - FValidationLengths[AStartIndex, AStopIndex + 1];
{$ifdef debug}
Write(count, '/', ASequenceLength, '/', freedoms, ' ');
{$endif}
if freedoms >= 0 then
Result := BinomialCoefficients.Get(count + freedoms, freedoms)
else
Result := 0;
end
else begin
Result := 1;
{$ifdef debug}
Write('X ');
{$endif}
end;
end;
{ THotSprings } { THotSprings }
constructor THotSprings.Create; constructor THotSprings.Create;
begin begin
FDebugIndex := 0; FDebugIndex := 0;
FBinomialCoefficients := TBinomialCoefficientCache.Create;
end; end;
destructor THotSprings.Destroy; destructor THotSprings.Destroy;
begin begin
FBinomialCoefficients.Free;
inherited Destroy; inherited Destroy;
end; end;
@ -736,8 +766,8 @@ begin
WriteLn; WriteLn;
{$endif} {$endif}
conditionRecord1 := TConditionRecord.Create(FBinomialCoefficients); conditionRecord1 := TConditionRecord.Create;
conditionRecord2 := TConditionRecord.Create(FBinomialCoefficients); conditionRecord2 := TConditionRecord.Create;
mainSplit := ALine.Split([' ']); mainSplit := ALine.Split([' ']);