diff --git a/dotnet/WhiteRabbit/VectorsProcessor.cs b/dotnet/WhiteRabbit/VectorsProcessor.cs index 369f0ce..d9a5490 100644 --- a/dotnet/WhiteRabbit/VectorsProcessor.cs +++ b/dotnet/WhiteRabbit/VectorsProcessor.cs @@ -22,6 +22,20 @@ this.MaxVectorsCount = maxVectorsCount; this.Dictionary = ImmutableArray.Create(FilterVectors(dictionary, target).ToArray()); + + var normsIndex = new int[GetVectorNorm(target, target) + 1]; + var offset = 0; + for (var i = normsIndex.Length - 1; i >= 0; i--) + { + while (offset < this.Dictionary.Length && this.Dictionary[offset].Norm > i) + { + offset++; + } + + normsIndex[i] = offset; + } + + this.NormsIndex = ImmutableArray.Create(normsIndex); } private Vector Target { get; } @@ -30,6 +44,9 @@ private ImmutableArray Dictionary { get; } + // Stores index of the first vector from Dictionary with norm less than or equal to offset + private ImmutableArray NormsIndex { get; } + // Produces all sets of vectors with the target sum #if SINGLE_THREADED public IEnumerable GenerateSequences() @@ -37,7 +54,7 @@ public ParallelQuery GenerateSequences() #endif { - return GenerateUnorderedSequences(this.Target, GetVectorNorm(this.Target, this.Target), this.MaxVectorsCount, this.Dictionary, 0) + return this.GenerateUnorderedSequences(this.Target, GetVectorNorm(this.Target, this.Target), this.MaxVectorsCount, 0) #if !SINGLE_THREADED .AsParallel() #endif @@ -74,7 +91,7 @@ // 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. // 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 static IEnumerable> GenerateUnorderedSequences(Vector remainder, int remainderNorm, int allowedRemainingWords, ImmutableArray dictionary, int currentDictionaryPosition) + private IEnumerable> GenerateUnorderedSequences(Vector remainder, int remainderNorm, int allowedRemainingWords, int currentDictionaryPosition) { if (allowedRemainingWords > 1) { @@ -84,9 +101,9 @@ // we need the largest remaining word to have a norm of at least 3 var requiredRemainderPerWord = (remainderNorm + allowedRemainingWords - 1) / allowedRemainingWords; - for (var i = FindFirstWithNormLessOrEqual(remainderNorm, dictionary, currentDictionaryPosition); i < dictionary.Length; i++) + for (var i = Math.Max(this.NormsIndex[remainderNorm], currentDictionaryPosition); i < this.Dictionary.Length; i++) { - var currentVectorInfo = dictionary[i]; + var currentVectorInfo = this.Dictionary[i]; if (currentVectorInfo.Vector == remainder) { yield return ImmutableStack.Create(currentVectorInfo.Index); @@ -99,7 +116,7 @@ { var newRemainder = remainder - currentVectorInfo.Vector; var newRemainderNorm = remainderNorm - currentVectorInfo.Norm; - foreach (var result in GenerateUnorderedSequences(newRemainder, newRemainderNorm, newAllowedRemainingWords, dictionary, i)) + foreach (var result in this.GenerateUnorderedSequences(newRemainder, newRemainderNorm, newAllowedRemainingWords, i)) { yield return result.Push(currentVectorInfo.Index); } @@ -108,9 +125,9 @@ } else { - for (var i = FindFirstWithNormLessOrEqual(remainderNorm, dictionary, currentDictionaryPosition); i < dictionary.Length; i++) + for (var i = Math.Max(this.NormsIndex[remainderNorm], currentDictionaryPosition); i < this.Dictionary.Length; i++) { - var currentVectorInfo = dictionary[i]; + var currentVectorInfo = this.Dictionary[i]; if (currentVectorInfo.Vector == remainder) { yield return ImmutableStack.Create(currentVectorInfo.Index); @@ -123,41 +140,6 @@ } } - // BCL BinarySearch would find any vector with required norm, not the first one; or would find nothing if there is no such vector - private static int FindFirstWithNormLessOrEqual(int expectedNorm, ImmutableArray dictionary, int offset) - { - var start = offset; - var end = dictionary.Length - 1; - - if (dictionary[start].Norm <= expectedNorm) - { - return start; - } - - if (dictionary[end].Norm > expectedNorm) - { - return dictionary.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 - // The loop always ends, because the difference always decreases; if start + 1 = end, then middle will be equal to start, and either end := middle = start or start := middle + 1 = end. - while (start < end) - { - var middle = (start + end) / 2; - var newNorm = dictionary[middle].Norm; - if (dictionary[middle].Norm <= expectedNorm) - { - end = middle; - } - else - { - start = middle + 1; - } - } - - return start; - } - private struct VectorInfo { public VectorInfo(Vector vector, int norm, int index)