@ -3,7 +3,6 @@
using System ;
using System ;
using System.Collections.Generic ;
using System.Collections.Generic ;
using System.Collections.Immutable ;
using System.Collections.Immutable ;
using System.Diagnostics ;
using System.Linq ;
using System.Linq ;
using System.Numerics ;
using System.Numerics ;
@ -45,10 +44,10 @@
// Produces all sequences of vectors with the target sum
// Produces all sequences of vectors with the target sum
public ParallelQuery < Vector < byte > [ ] > GenerateSequences ( )
public ParallelQuery < Vector < byte > [ ] > GenerateSequences ( )
{
{
return this . GenerateUnorderedSequences ( this . Target , GetVectorNorm ( this . Target , this . Target ) , this . MaxVectorsCount , 0 )
return GenerateUnorderedSequences ( this . Target , GetVectorNorm ( this . Target , this . Target ) , this . MaxVectorsCount , this . Dictionary , 0 )
. AsParallel ( )
. AsParallel ( )
. Select ( Enumerable . ToArray )
. Select ( Enumerable . ToArray )
. SelectMany ( this . GeneratePermutations ) ;
. SelectMany ( GeneratePermutations ) ;
}
}
// We want words with more letters (and among these, words with more "rare" letters) to appear first, to reduce the searching time somewhat.
// We want words with more letters (and among these, words with more "rare" letters) to appear first, to reduce the searching time somewhat.
@ -77,21 +76,11 @@
. ToArray ( ) ;
. ToArray ( ) ;
}
}
[Conditional("DEBUG")]
private void DebugState ( int allowedRemainingWords , Vector < byte > currentVector )
{
this . Iterations + + ;
if ( this . Iterations % 1 0 0 0 0 0 0 = = 0 )
{
Console . WriteLine ( $"Iteration #{this.Iterations}: {allowedRemainingWords}, {this.VectorToString(currentVector)}" ) ;
}
}
// This method takes most of the time, so everything related to it must be optimized.
// This method takes most of the time, so everything related to it must be optimized.
// In every sequence, next vector always goes after the previous one from dictionary.
// In every sequence, next vector always goes after the previous one from dictionary.
// E.g. if dictionary is [x, y, z], then only [x, y] sequence could be generated, and [y, x] will never be generated.
// E.g. if dictionary is [x, y, z], then only [x, y] sequence could be generated, and [y, x] will never be generated.
// That way, the complexity of search goes down by a factor of MaxVectorsCount! (as if [x, y] does not add up to a required target, there is no point in checking [y, x])
// That way, the complexity of search goes down by a factor of MaxVectorsCount! (as if [x, y] does not add up to a required target, there is no point in checking [y, x])
private IEnumerable < ImmutableStack < Vector < byte > > > GenerateUnorderedSequences ( Vector < byte > remainder , int remainderNorm , int allowedRemainingWords , int currentDictionaryPosition )
private static IEnumerable < ImmutableStack < Vector < byte > > > GenerateUnorderedSequences ( Vector < byte > remainder , int remainderNorm , int allowedRemainingWords , ImmutableArray < VectorInfo > dictionary , int currentDictionaryPosition )
{
{
if ( allowedRemainingWords > 1 )
if ( allowedRemainingWords > 1 )
{
{
@ -100,12 +89,9 @@
// we need the largest remaining word to have a norm of at least 3
// we need the largest remaining word to have a norm of at least 3
var requiredRemainderPerWord = ( remainderNorm + allowedRemainingWords - 1 ) / allowedRemainingWords ;
var requiredRemainderPerWord = ( remainderNorm + allowedRemainingWords - 1 ) / allowedRemainingWords ;
for ( var i = FindFirstWithNormLessOrEqual ( remainderNorm , currentDictionaryPosition ) ; i < this . D ictionary. Length ; i + + )
for ( var i = FindFirstWithNormLessOrEqual ( remainderNorm , dictionary , currentDictionaryPosition ) ; i < d ictionary. Length ; i + + )
{
{
var currentVectorInfo = this . Dictionary [ i ] ;
var currentVectorInfo = dictionary [ i ] ;
this . DebugState ( allowedRemainingWords , currentVectorInfo . Vector ) ;
if ( currentVectorInfo . Vector = = remainder )
if ( currentVectorInfo . Vector = = remainder )
{
{
yield return ImmutableStack . Create ( currentVectorInfo . Vector ) ;
yield return ImmutableStack . Create ( currentVectorInfo . Vector ) ;
@ -118,7 +104,7 @@
{
{
var newRemainder = remainder - currentVectorInfo . Vector ;
var newRemainder = remainder - currentVectorInfo . Vector ;
var newRemainderNorm = remainderNorm - currentVectorInfo . Norm ;
var newRemainderNorm = remainderNorm - currentVectorInfo . Norm ;
foreach ( var result in this . GenerateUnorderedSequences ( newRemainder , newRemainderNorm , newAllowedRemainingWords , i ) )
foreach ( var result in GenerateUnorderedSequences ( newRemainder , newRemainderNorm , newAllowedRemainingWords , dictionary , i ) )
{
{
yield return result . Push ( currentVectorInfo . Vector ) ;
yield return result . Push ( currentVectorInfo . Vector ) ;
}
}
@ -127,12 +113,9 @@
}
}
else
else
{
{
for ( var i = FindFirstWithNormLessOrEqual ( remainderNorm , currentDictionaryPosition ) ; i < this . D ictionary. Length ; i + + )
for ( var i = FindFirstWithNormLessOrEqual ( remainderNorm , dictionary , currentDictionaryPosition ) ; i < d ictionary. Length ; i + + )
{
{
var currentVectorInfo = this . Dictionary [ i ] ;
var currentVectorInfo = dictionary [ i ] ;
this . DebugState ( allowedRemainingWords , currentVectorInfo . Vector ) ;
if ( currentVectorInfo . Vector = = remainder )
if ( currentVectorInfo . Vector = = remainder )
{
{
yield return ImmutableStack . Create ( currentVectorInfo . Vector ) ;
yield return ImmutableStack . Create ( currentVectorInfo . Vector ) ;
@ -146,19 +129,19 @@
}
}
// BCL BinarySearch would find any vector with required norm, not the first one; or would find nothing if there is no such vector
// BCL BinarySearch would find any vector with required norm, not the first one; or would find nothing if there is no such vector
private int FindFirstWithNormLessOrEqual ( int expectedNorm , int offset )
private static int FindFirstWithNormLessOrEqual ( int expectedNorm , ImmutableArray < VectorInfo > dictionary , int offset )
{
{
var start = offset ;
var start = offset ;
var end = this . D ictionary. Length - 1 ;
var end = d ictionary. Length - 1 ;
if ( this . D ictionary[ start ] . Norm < = expectedNorm )
if ( d ictionary[ start ] . Norm < = expectedNorm )
{
{
return start ;
return start ;
}
}
if ( this . D ictionary[ end ] . Norm > expectedNorm )
if ( d ictionary[ end ] . Norm > expectedNorm )
{
{
return this . D ictionary. Length ;
return d ictionary. Length ;
}
}
// Norm for start is always greater than expected norm, or start is the required position; norm for end is always less than or equal to expected norm
// Norm for start is always greater than expected norm, or start is the required position; norm for end is always less than or equal to expected norm
@ -166,8 +149,8 @@
while ( start < end )
while ( start < end )
{
{
var middle = ( start + end ) / 2 ;
var middle = ( start + end ) / 2 ;
var newNorm = this . D ictionary[ middle ] . Norm ;
var newNorm = d ictionary[ middle ] . Norm ;
if ( this . D ictionary[ middle ] . Norm < = expectedNorm )
if ( d ictionary[ middle ] . Norm < = expectedNorm )
{
{
end = middle ;
end = middle ;
}
}
@ -180,7 +163,7 @@
return start ;
return start ;
}
}
private IEnumerable < T [ ] > GeneratePermutations < T > ( T [ ] original )
private static IEnumerable < T [ ] > GeneratePermutations < T > ( T [ ] original )
{
{
foreach ( var permutation in PrecomputedPermutationsGenerator . HamiltonianPermutations ( original . Length ) )
foreach ( var permutation in PrecomputedPermutationsGenerator . HamiltonianPermutations ( original . Length ) )
{
{