From ec6928679ab1f42612bb524d08e6663b2747e794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Sun, 17 Nov 2024 23:54:08 +0100 Subject: [PATCH] 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 --- UCommon.pas | 1 + solvers/UHotSprings.pas | 230 +++++++++++++++++++++++----------------- 2 files changed, 131 insertions(+), 100 deletions(-) diff --git a/UCommon.pas b/UCommon.pas index 00961f0..4380962 100644 --- a/UCommon.pas +++ b/UCommon.pas @@ -41,6 +41,7 @@ const type TIntegerList = specialize TList; + TInt64Array = array of Int64; implementation diff --git a/solvers/UHotSprings.pas b/solvers/UHotSprings.pas index c82c383..a7b47b6 100644 --- a/solvers/UHotSprings.pas +++ b/solvers/UHotSprings.pas @@ -63,9 +63,9 @@ type end; TBlocks = specialize TObjectList; - { TValidationToDamageAssignments } + { TDamageToValidationAssignments } - TValidationToDamageAssignments = class(TEnumerableMultiIndexStrategy) + TDamageToValidationAssignments = class(TEnumerableMultiIndexStrategy) private FValidation: TIntegerList; FValidationLengths: TValidationLengths; @@ -92,14 +92,34 @@ type TValidationPositionInfos = specialize TList; + 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 = class(TEnumerableMultiIndexStrategy) + TValidationPositionOffsets = class(TAccumulatedCombinationsMultiIndexStrategy) private - FValidation: TIntegerList; + FConditionRecord: TConditionRecord; FPositionInfos: TValidationPositionInfos; + FBlockLength, FStartIndex, FStopIndex: Integer; + protected + function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; override; 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 TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer; out AStartIndexValue: Integer): Boolean; override; @@ -111,7 +131,6 @@ type TConditionRecord = class private - FBinomialCoefficients: TBinomialCoefficientCache; FValidation: TIntegerList; // List of non-empty, maximum-length parts of the pattern without operational springs ("blocks"). FBlocks: TBlocks; @@ -129,17 +148,13 @@ type function CalcCombinationsBlockSingleValidation(constref ABlock: TBlock; const AIndex: Integer): Int64; function CalcCombinationsBlockMultiValidations(constref ABlock: TBlock; constref AIndices: TIndexArray; 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 - constructor Create(constref ABinomialCoefficients: TBinomialCoefficientCache); + constructor Create; destructor Destroy; override; // Adds all non-empty, maximum-length parts of the pattern without operational springs ("blocks"). procedure AddBlocks(const APattern: string); function GenerateBlockAssignments: Int64; - // TODO: Blocks is not needed? - //property Blocks: TBlocks read FBlocks; + function CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer): Int64; property Validation: TIntegerList read FValidation; end; @@ -147,8 +162,6 @@ type THotSprings = class(TSolver) private - // Keeping the binomial coefficients calculator here so it can be shared for all lines. - FBinomialCoefficients: TBinomialCoefficientCache; // TODO: Remove FDebugIndex. FDebugIndex: Integer; public @@ -206,9 +219,9 @@ begin inherited Destroy; end; -{ TValidationToDamageAssignments } +{ TDamageToValidationAssignments } -function TValidationToDamageAssignments.CalcValidationSpan(constref ACurrentIndexArray: TIndexArray; +function TDamageToValidationAssignments.CalcValidationSpan(constref ACurrentIndexArray: TIndexArray; const ALastDamageIndex, AValidationNumber: Integer): Integer; var spanStart: Integer; @@ -221,7 +234,7 @@ begin Inc(Result, FDamages[ALastDamageIndex].Start - FDamages[spanStart].Start); 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); begin FValidation := AValidation; @@ -231,12 +244,12 @@ begin FValidationStopIndex := AStopIndex; end; -function TValidationToDamageAssignments.GetCardinality: Integer; +function TDamageToValidationAssignments.GetCardinality: Integer; begin Result := FDamages.Count; end; -function TValidationToDamageAssignments.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; +function TDamageToValidationAssignments.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer; out AStartIndexValue: Integer): Boolean; begin Result := True; @@ -246,7 +259,7 @@ begin AStartIndexValue := FValidationStartIndex; end; -function TValidationToDamageAssignments.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; +function TDamageToValidationAssignments.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult; var i, prev, firstSkip: Integer; @@ -296,13 +309,71 @@ begin Result := ivrValid; 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 } -constructor TValidationPositionOffsets.Create(constref AValidation: TIntegerList; constref APositionInfos: - TValidationPositionInfos); +function TValidationPositionOffsets.CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: + Integer): Int64; +var + space, start, stop: Integer; 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; + FBlockLength := ABlockLength; + FStartIndex := AStartIndex; + FStopIndex := AStopIndex; + inherited Create; end; function TValidationPositionOffsets.GetCardinality: Integer; @@ -317,13 +388,12 @@ var begin info := FPositionInfos[ACurrentIndex]; // Calculates start value such that the validation number just includes MinEnd. - //AStartIndexValue := info.MinEnd - FValidation[info.ValidationIndex] + 1; AStartIndexValue := info.MinStart; // Adjusts start value to avoid overlap of this validation number with the previous one (the one from previous // position info). if ACurrentIndex > 0 then AStartIndexValue := Max(AStartIndexValue, - ACurrentIndexArray[ACurrentIndex - 1] + FValidation[FPositionInfos[ACurrentIndex - 1].ValidationIndex] + 1); + ACurrentIndexArray[ACurrentIndex - 1] + FConditionRecord.Validation[FPositionInfos[ACurrentIndex - 1].ValidationIndex] + 1); Result := True; end; @@ -334,6 +404,8 @@ begin Result := ivrValid else Result := ivrBacktrack; + + Result := UpdateCombinations(Result, ACurrentIndexArray, ACurrentIndex); end; { TConditionRecord } @@ -413,7 +485,7 @@ function TConditionRecord.CalcCombinationsBlock(constref ABlock: TBlock; const A var i, j, k: Integer; indices: TIndexArray; - validationToDamageAssignments: TValidationToDamageAssignments; + validationToDamageAssignments: TDamageToValidationAssignments; begin {$ifdef debug} Write(' ', ABlock.Pattern, ' '); @@ -464,7 +536,7 @@ begin Result := 0; // Assigns validation numbers to specific damages. - validationToDamageAssignments := TValidationToDamageAssignments.Create(FValidation, FValidationLengths, ABlock.Damages, + validationToDamageAssignments := TDamageToValidationAssignments.Create(FValidation, FValidationLengths, ABlock.Damages, AStartIndex, AStopIndex); {$ifdef debug} WriteLn(' validation numbers (indices) per damages:'); @@ -551,82 +623,17 @@ begin WriteLn(' offsets'); {$endif} Result := 0; - validationPositionOffsets := TValidationPositionOffsets.Create(FValidation, positions); + validationPositionOffsets := TValidationPositionOffsets.Create(Self, positions, Length(ABlock.Pattern), + AStartIndex, AStopIndex); for offsets in validationPositionOffsets do - Result := Result - + CalcCombinationsBlockAssignedValidations(Length(ABlock.Pattern), positions, offsets, AStartIndex, AStopIndex); + Result := Result + validationPositionOffsets.GetCombinations; validationPositionOffsets.Free; positions.Free; end; -function TConditionRecord.CalcCombinationsBlockAssignedValidations(const ABlockLength: Integer; constref APositionInfos: - TValidationPositionInfos; constref AOffsets: TIndexArray; const AStartIndex, AStopIndex: Integer): Int64; -var - i, space: Integer; +constructor TConditionRecord.Create; 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; FValidation := TIntegerList.Create; end; @@ -710,17 +717,40 @@ begin WriteLn(' missed: ', misses, '/', count); 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 } constructor THotSprings.Create; begin FDebugIndex := 0; - FBinomialCoefficients := TBinomialCoefficientCache.Create; end; destructor THotSprings.Destroy; begin - FBinomialCoefficients.Free; inherited Destroy; end; @@ -736,8 +766,8 @@ begin WriteLn; {$endif} - conditionRecord1 := TConditionRecord.Create(FBinomialCoefficients); - conditionRecord2 := TConditionRecord.Create(FBinomialCoefficients); + conditionRecord1 := TConditionRecord.Create; + conditionRecord2 := TConditionRecord.Create; mainSplit := ALine.Split([' ']);